<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="9788793609624.xsl"?>
<book id="home" xmlns:xlink="http://www.w3.org/1999/xlink">
<bookinfo>
<title>High-Performance and Time-Predictable Embedded Computing</title>
<affiliation><emphasis role="strong">Editors</emphasis></affiliation>
<authorgroup>
<author><firstname>Lu&#x000ED;s Miguel</firstname>
<surname>Pinho</surname>
</author>
</authorgroup>
<affiliation>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</affiliation>
<authorgroup>
<author><firstname>Eduardo</firstname>
<surname>Qui&#x000F1;ones</surname>
</author>
</authorgroup>
<affiliation>Barcelona Supercomputing Center, Spain</affiliation>
<authorgroup>
<author><firstname>Marko</firstname>
<surname>Bertogna</surname>
</author>
</authorgroup>
<affiliation>University of Modena and Reggio Emilia, Italy</affiliation>
<authorgroup>
<author><firstname>Andrea</firstname>
<surname>Marongiu</surname>
</author>
</authorgroup>
<affiliation>Swiss Federal Institute of Technology Zurich, Switzerland</affiliation>
<authorgroup>
<author><firstname>Vincent</firstname>
<surname>N&#x000E9;lis</surname>
</author>
</authorgroup>
<affiliation>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</affiliation>
<authorgroup>
<author><firstname>Paolo</firstname>
<surname>Gai</surname>
</author>
</authorgroup>
<affiliation>Evidence Srl, Italy</affiliation>
<authorgroup>
<author><firstname>Juan</firstname>
<surname>Sancho</surname>
</author>
</authorgroup>
<affiliation>ATOS, Spain</affiliation>
<publisher>
<publishername>River Publishers</publishername>
</publisher>
<isbn>9788793609624</isbn>
</bookinfo>
<preface class="preface" id="preface01">
<title>RIVER PUBLISHERS SERIES IN INFORMATION SCIENCE AND TECHNOLOGY</title>
<para><emphasis>Series Editors</emphasis></para>
<para><emphasis role="strong">K. C. CHEN</emphasis><?lb?><emphasis>National Taiwan University</emphasis><?lb?><emphasis>Taipei, Taiwan</emphasis><?lb?>and<?lb?><emphasis>University of South Florida, USA</emphasis></para>
<para><emphasis role="strong">SANDEEP SHUKLA</emphasis><?lb?><emphasis>Virginia Tech</emphasis><?lb?><emphasis>USA</emphasis><?lb?>and<?lb?><emphasis>Indian Institute of Technology Kanpur, India</emphasis></para>
<para>Indexing: All books published in this series are submitted to the Web of Science Book Citation Index (BkCI), to CrossRef and to Google Scholar.</para>
<para>The &#x0201C;River Publishers Series in Information Science and Technology&#x0201D; covers research which ushers the 21st Century into an Internet and multimedia era. Multimedia means the theory and application of filtering, coding, estimating, analyzing, detecting and recognizing, synthesizing, classifying, recording, and reproducing signals by digital and/or analog devices or techniques, while the scope of &#x0201C;signal&#x0201D; includes audio, video, speech, image, musical, multimedia, data/content, geophysical, sonar/radar, bio/medical, sensation, etc. Networking suggests transportation of such multimedia contents among nodes in communication and/or computer networks, to facilitate the ultimate Internet.</para>
<para>Theory, technologies, protocols and standards, applications/services, practice and implementation of wired/wireless networking are all within the scope of this series. Based on network and communication science, we further extend the scope for 21st Century life through the knowledge in robotics, machine learning, embedded systems, cognitive science, pattern recognition, quantum/biological/molecular computation and information processing, biology, ecology, social science and economics, user behaviors and interface, and applications to health and society advance.</para>
<para>Books published in the series include research monographs, edited volumes, handbooks and textbooks. The books provide professionals, researchers, educators, and advanced students in the field with an invaluable insight into the latest research and developments.</para>
<para>Topics covered in the series include, but are by no means restricted to the following:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Communication/Computer Networking Technologies and Applications</para></listitem>
<listitem>
<para>Queuing Theory</para></listitem>
<listitem>
<para>Optimization</para></listitem>
<listitem>
<para>Operation Research</para></listitem>
<listitem>
<para>Stochastic Processes</para></listitem>
<listitem>
<para>Information Theory</para></listitem>
<listitem>
<para>Multimedia/Speech/Video Processing</para></listitem>
<listitem>
<para>Computation and Information Processing</para></listitem>
<listitem>
<para>Machine Intelligence</para></listitem>
<listitem>
<para>Cognitive Science and Brian Science</para></listitem>
<listitem>
<para>Embedded Systems</para></listitem>
<listitem>
<para>Computer Architectures</para></listitem>
<listitem>
<para>Reconfigurable Computing</para></listitem>
<listitem>
<para>Cyber Security</para></listitem>
</itemizedlist>
<para>For a list of other books in this series, visit <ulink url="http://www.riverpublishers.com">www.riverpublishers.com</ulink></para>
</preface>
<preface class="preface" id="preface02">
<title>Preface</title>
<para>Nowadays, the prevalence of electronic and computing systems in our lives is so ubiquitous that it would not be far-fetched to state that we live in a cyber-physical world dominated by computer systems. Examples include pacemakers implanted within the human body to regulate and monitor heartbeats, cars and airplanes transporting us, smart grids, and traffic management.</para>
<para>All these systems demand more and more computational performance to process large amounts of data from multiple data sources, and some of them with guaranteed processing response times; in other words, systems required to deliver their results within pre-defined (and sometimes extremely short) time bounds. This timing aspect is vital for systems like planes, cars, business monitoring, e-trading, etc. Examples can be found in intelligent transportation systems for fuel consumption reduction in cities or railways, or autonomous driving of vehicles. All these systems require processing and actuation based on large amounts of data coming from real-time sensor information.</para>
<para>As a result, the computer electronic devices which these systems depend on are constantly required to become more and more powerful and reliable, while remaining affordable. In order to cope with such performance requirements, chip designers have recently started producing chips containing multiple processing units, the so-called multi-core processors, effectively integrating multiple computers within a single chip, and more recently the many-core processors, with dozens or hundreds of cores, interconnected with complex networks on chip. This radical shift in the chip design paved the way for parallel computing: rather than processing the data sequentially, the cooperation of multiple processing elements within the same chip allows systems to be executed concurrently, in parallel.</para>
<para>Unfortunately, the parallelization of the computing activities brought up many challenges, because it affects the timing behavior of the systems as well as the entire way people think and design computers: from the design of the hardware architecture, through the operating system up to the conceptualization of the end-user application. Therefore, although many-core processors are promising candidates to improve the responsiveness of these systems, the interactions that the different computing elements may have within the chip can seriously affect the performance opportunities brought by parallel execution. Moreover, providing timing guarantees becomes harder, because the timing behavior of the system running within a many-core processor depends on interactions that are most of the time not known by the system designer. This makes system analysts struggle in trying to provide timing guarantees for such platforms. Finally, most of the optimization mechanisms buried deep inside the chip are geared only to increase performance and execution speed rather than providing predictable time behavior.</para>
<para>These challenges need to be addressed by introducing predictability in the vertical stack from high-level programming models to operating systems, together with new offine analysis techniques. This book covers the main techniques to enable performance and predictability of embedded applications. The book starts with an overview of some of the current many-core embedded platforms, and then addresses how to support predictability and performance in different aspects of computation: a predictable parallel programming model, the mapping and scheduling of real-time parallel computation, the timing analysis of parallel code, as well as the techniques to support predictability in parallel runtimes and operating systems.</para>
<para>The work reflected in this book was done in the scope of the European project P-SOCRATES, funded under the FP7 framework program of the European Commission. The project website (<ulink url="http://www.p-socrates.eu">www.p-socrates.eu</ulink>), provides further detailed information on the techniques presented here. Moreover, a reference implementation of the methodologies and tools was released as the UpScale Software Development Kit (<ulink url="http://www.upscale-sdk.com">http://www.upscale-sdk.com</ulink>).</para>
<blockquote>
<para>Lu&#x000ED;s Miguel Pinho<?lb?>Eduardo Qui&#x000F1;ones<?lb?>Marko Bertogna<?lb?>Andrea Marongiu<?lb?>Vincent N&#x000E9;lis<?lb?>Paolo Gai<?lb?>Juan Sancho<?lb?></para>
<para>February 2018</para></blockquote>
</preface>
<preface class="preface" id="preface03">
<title>List of Contributors</title>
<para><emphasis role="strong">Alessandra Melani</emphasis>, <emphasis>University of Modena and Reggio Emilia, Italy</emphasis></para>
<para><emphasis role="strong">Andrea Marongiu</emphasis>, <emphasis>Swiss Federal Institute of Technology in Z&#x000FC;rich (ETHZ), Switzerland; and University of Bologna, Italy</emphasis></para>
<para><emphasis role="strong">Bruno Morelli</emphasis>, <emphasis>Evidence SRL, Italy</emphasis></para>
<para><emphasis role="strong">Claudio Scordino</emphasis>, <emphasis>Evidence SRL, Italy</emphasis></para>
<para><emphasis role="strong">Eduardo Qui&#x000F1;ones</emphasis>, <emphasis>Barcelona Supercomputing Center (BSC), Spain</emphasis></para>
<para><emphasis role="strong">Errico Guidieri</emphasis>, <emphasis>Evidence SRL, Italy</emphasis></para>
<para><emphasis role="strong">Giuseppe Tagliavini</emphasis>, <emphasis>University of Bologna, Italy</emphasis></para>
<para><emphasis role="strong">Juan Sancho</emphasis>, <emphasis>ATOS, Spain</emphasis></para>
<para><emphasis role="strong">Lu&#x000ED;s Miguel Pinho</emphasis>, <emphasis>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</emphasis></para>
<para><emphasis role="strong">Maria A. Serrano</emphasis>, <emphasis>Barcelona Supercomputing Center (BSC), Spain</emphasis></para>
<para><emphasis role="strong">Marko Bertogna</emphasis>, <emphasis>University of Modena and Reggio Emilia, Italy</emphasis></para>
<para><emphasis role="strong">Paolo Burgio</emphasis>, <emphasis>University of Modena and Reggio Emilia, Italy</emphasis></para> 
<para><emphasis role="strong">Paolo Gai</emphasis>, <emphasis>Evidence SRL, Italy</emphasis></para>
<para><emphasis role="strong">Patrick Meumeu Yomsi</emphasis>, <emphasis>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</emphasis></para> 
<para><emphasis role="strong">Sara Royuela</emphasis>, <emphasis>Barcelona Supercomputing Center (BSC), Spain</emphasis></para>
<para><emphasis role="strong">Vincent N&#x000E9;lis</emphasis>, <emphasis>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</emphasis></para>
</preface>
<preface class="preface" id="preface04">
<title>List of Figures</title>
<table-wrap position="float">
<table cellspacing="5" cellpadding="5" frame="none" rules="none">
<tbody>
<tr><td valign="top" width="15%"><emphasis role="strong"><link linkend="F1-1">Figure <xref linkend="F1-1" remap="1.1"/></link></emphasis></td><td valign="top" align="left">P-SOCRATES Global perspective.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F1-2">Figure <xref linkend="F1-2" remap="1.2"/></link></emphasis></td><td valign="top" align="left">P-SOCRATES combines high-performance parallel programming models, high-end embedded many-core platforms and real-time systems technology.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F1-3">Figure <xref linkend="F1-3" remap="1.3"/></link></emphasis></td><td valign="top" align="left">Vertical stack of application decomposition.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F1-4">Figure <xref linkend="F1-4" remap="1.4"/></link></emphasis></td><td valign="top" align="left">The UpScale SDK.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-1">Figure <xref linkend="F2-1" remap="2.1"/></link></emphasis></td><td valign="top" align="left">Knights Landing (KNL) block diagram: (a) the CPU, (b) an example tile, and (c) KNL with Omni-Path Fabric integrated on the CPU package.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-2">Figure <xref linkend="F2-2" remap="2.2"/></link></emphasis></td><td valign="top" align="left">PEZY-SC architecture block diagram.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-3">Figure <xref linkend="F2-3" remap="2.3"/></link></emphasis></td><td valign="top" align="left">NVIDIA Tegra X1 block diagram.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-4">Figure <xref linkend="F2-4" remap="2.4"/></link></emphasis></td><td valign="top" align="left">Tilera <emphasis>Tile</emphasis> architectural template.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-5">Figure <xref linkend="F2-5" remap="2.5"/></link></emphasis></td><td valign="top" align="left">STMicroelectronics STHORM heterogeneous system.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-6">Figure <xref linkend="F2-6" remap="2.6"/></link></emphasis></td><td valign="top" align="left">Block diagram of the Epiphany-V chip from Adapteva.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-7">Figure <xref linkend="F2-7" remap="2.7"/></link></emphasis></td><td valign="top" align="left">Texas Instrument Keystone II heterogeneous system.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-8">Figure <xref linkend="F2-8" remap="2.8"/></link></emphasis></td><td valign="top" align="left">High-level view of the Kalray MPPA-256 processor.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-9">Figure <xref linkend="F2-9" remap="2.9"/></link></emphasis></td><td valign="top" align="left">MPPA-256 NoC architecture.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-10">Figure <xref linkend="F2-10" remap="2.10"/></link></emphasis></td><td valign="top" align="left">A master task runs on an RM of an I/O subsystem.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-11">Figure <xref linkend="F2-11" remap="2.11"/></link></emphasis></td><td valign="top" align="left">Internal architecture of a compute cluster.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-12">Figure <xref linkend="F2-12" remap="2.12"/></link></emphasis></td><td valign="top" align="left">Memory accesses distributed across memory banks (interleaved).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F2-13">Figure <xref linkend="F2-13" remap="2.13"/></link></emphasis></td><td valign="top" align="left">Memory accesses targeting a same memory bank (contiguous).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-1">Figure <xref linkend="F3-1" remap="3.1"/></link></emphasis></td><td valign="top" align="left">OpenMP components stack.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-2">Figure <xref linkend="F3-2" remap="3.2"/></link></emphasis></td><td valign="top" align="left">OpenMP releases time-line.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-3">Figure <xref linkend="F3-3" remap="3.3"/></link></emphasis></td><td valign="top" align="left">Structured parallelism.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-4">Figure <xref linkend="F3-4" remap="3.4"/></link></emphasis></td><td valign="top" align="left">Unstructured parallelism.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link></emphasis></td><td valign="top" align="left">OpenMP-DAG composed of <emphasis>task parts</emphasis> based on the code.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-6">Figure <xref linkend="F3-6" remap="3.6"/></link></emphasis></td><td valign="top" align="left">DAG composed on task region parts.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-7">Figure <xref linkend="F3-7" remap="3.7"/></link></emphasis></td><td valign="top" align="left">aDAG of the OpenMP program.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F3-8">Figure <xref linkend="F3-8" remap="3.8"/></link></emphasis></td><td valign="top" align="left">The DAG of the OpenMP program</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-1">Figure <xref linkend="F4-1" remap="4.1"/></link></emphasis></td><td valign="top" align="left">An application is composed of multiple real-time tasks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-2">Figure <xref linkend="F4-2" remap="4.2"/></link></emphasis></td><td valign="top" align="left">RT tasks are mapped to OS threads, which are scheduled on the processing elements.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-3">Figure <xref linkend="F4-3" remap="4.3"/></link></emphasis></td><td valign="top" align="left">Fully preemptive vs. non-preemptive scheduling: preemption overhead and blocking delay may cause deadline misses.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-4">Figure <xref linkend="F4-4" remap="4.4"/></link></emphasis></td><td valign="top" align="left">A sample cp-task. Each vertex is labeled with the WCET of the corresponding sub-task.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-5">Figure <xref linkend="F4-5" remap="4.5"/></link></emphasis></td><td valign="top" align="left">Work-case scenario to maximize the workload of an task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, in the sequential case.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-6">Figure <xref linkend="F4-6" remap="4.6"/></link></emphasis></td><td valign="top" align="left">Worst-case scenario to maximize the workload of an interfering cp-task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link></emphasis></td><td valign="top" align="left">DAGs of lp(k) tasks; the C<subscript>i,j</subscript> of each node <emphasis>v</emphasis><subscript>i,j</subscript> is presented in parenthesis.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-8">Figure <xref linkend="F4-8" remap="4.8"/></link></emphasis></td><td valign="top" align="left">Tasks example.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F4-9">Figure <xref linkend="F4-9" remap="4.9"/></link></emphasis></td><td valign="top" align="left">Different release patterns for the example of <link linkend="F4-8">Figure <xref linkend="F4-8" remap="4.8"/></link>. (a) represents the most optimistic case, while (c) the most pessimistic, i.e., yelding to the highest WCET. (b) represents an intermediate case.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F5-1">Figure <xref linkend="F5-1" remap="5.1"/></link></emphasis></td><td valign="top" align="left">Example distribution of execution time.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F5-2">Figure <xref linkend="F5-2" remap="5.2"/></link></emphasis></td><td valign="top" align="left">Extended task dependency graph (eTDG) of an example application.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F5-3">Figure <xref linkend="F5-3" remap="5.3"/></link></emphasis></td><td valign="top" align="left">Impact of an unused variable on the execution time of an example application.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-1">Figure <xref linkend="F6-1" remap="6.1"/></link></emphasis></td><td valign="top" align="left">Timing diagram of an offoading procedure.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-2">Figure <xref linkend="F6-2" remap="6.2"/></link></emphasis></td><td valign="top" align="left">Task suspension in the baseline implementation (considering tied tasks and WFS).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-3">Figure <xref linkend="F6-3" remap="6.3"/></link></emphasis></td><td valign="top" align="left">Untied task suspension with task contexts and per-task stacks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-4">Figure <xref linkend="F6-4" remap="6.4"/></link></emphasis></td><td valign="top" align="left">On the left (a), the DAG of an OpenMP program. On the right (b), the sparse matrix data structure implementing DAG shown on the left.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-5">Figure <xref linkend="F6-5" remap="6.5"/></link></emphasis></td><td valign="top" align="left">Costs of offoad initialization.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-6">Figure <xref linkend="F6-6" remap="6.6"/></link></emphasis></td><td valign="top" align="left">Speedup of the LINEAR benchmark (no cutoff).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-7">Figure <xref linkend="F6-7" remap="6.7"/></link></emphasis></td><td valign="top" align="left">Speedup of the RECURSIVE benchmark (no cutoff).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-8">Figure <xref linkend="F6-8" remap="6.8"/></link></emphasis></td><td valign="top" align="left">Structure of the MIXED microbenchmark.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-9">Figure <xref linkend="F6-9" remap="6.9"/></link></emphasis></td><td valign="top" align="left">Speedup of the MIXED benchmark.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-10">Figure <xref linkend="F6-10" remap="6.10"/></link></emphasis></td><td valign="top" align="left">Speedup of the LINEAR benchmark (with cutoff).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-11">Figure <xref linkend="F6-11" remap="6.11"/></link></emphasis></td><td valign="top" align="left">Speedup of the RECURSIVE benchmark (with cutoff).</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-12">Figure <xref linkend="F6-12" remap="6.12"/></link></emphasis></td><td valign="top" align="left">Speedups for the BOTS benchmarks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link></emphasis></td><td valign="top" align="left">Performance speedup of the Cholesky (a) and r3DPP (b) running with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link></emphasis></td><td valign="top" align="left">Memory usage (in KB) of the Cholesky (a) r3DPP (b) running with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F6-15">Figure <xref linkend="F6-15" remap="6.15"/></link></emphasis></td><td valign="top" align="left">Performance speedup of the Cholesky (a) and r3DPP (b) running on the MPPA with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F7-1">Figure <xref linkend="F7-1" remap="7.1"/></link></emphasis></td><td valign="top" align="left">Number of Linux-based supercomputers in the TOP500 list.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F7-2">Figure <xref linkend="F7-2" remap="7.2"/></link></emphasis></td><td valign="top" align="left">Structure of the multicore images in the original ERIKA Enterprise structure.</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="F7-3">Figure <xref linkend="F7-3" remap="7.3"/></link></emphasis></td><td valign="top" align="left">Structure of the Single-ELF image produced by ERIKA Enterprise.</td></tr>
</tbody>
</table>
</table-wrap>
</preface>
<preface class="preface" id="preface05">
<title>List of Tables</title>
<table-wrap position="float">
<table cellspacing="5" cellpadding="5" frame="none" rules="none">
<tbody>
<tr><td valign="top" width="15%"><emphasis role="strong"><link linkend="T3-1">Table <xref linkend="T3-1" remap="3.1"/></link></emphasis></td><td valign="top" align="left">Parallel programming models comparison</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T4-1">Table <xref linkend="T4-1" remap="4.1"/></link></emphasis></td><td valign="top" align="left">Worst-case workloads of tasks in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T4-2">Table <xref linkend="T4-2" remap="4.2"/></link></emphasis></td><td valign="top" align="left">Five possible scenarios of taskset in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link>, assuming a four core system</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T4-3">Table <xref linkend="T4-3" remap="4.3"/></link></emphasis></td><td valign="top" align="left">Computed worst-case workload for each of the scenarios in <link linkend="T4-2">Table <xref linkend="T4-2" remap="4.2"/></link></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T6-1">Table <xref linkend="T6-1" remap="6.1"/></link></emphasis></td><td valign="top" align="left">Memory usage of the sparse matrix (in KB), varying the number of tasks instantiated</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T7-1">Table <xref linkend="T7-1" remap="7.1"/></link></emphasis></td><td valign="top" align="left">Classification of RTOSs</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T7-2">Table <xref linkend="T7-2" remap="7.2"/></link></emphasis></td><td valign="top" align="left">ERIKA Enterprise footprint (expressed in bytes)</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T7-3">Table <xref linkend="T7-3" remap="7.3"/></link></emphasis></td><td valign="top" align="left">Timings (expressed in clock ticks)</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T7-4">Table <xref linkend="T7-4" remap="7.4"/></link></emphasis></td><td valign="top" align="left">Footprint comparison between ERIKA and NodeOS for a 16-core cluster (expressed in bytes)</td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="T7-5">Table <xref linkend="T7-5" remap="7.5"/></link></emphasis></td><td valign="top" align="left">Thread creation/activation times (expressed in clock ticks)</td></tr>
</tbody>
</table>
</table-wrap>
</preface>
<preface class="preface" id="preface06">
<title>List of Abbreviations</title>
<table-wrap position="float">
<table cellspacing="5" cellpadding="5" frame="none" rules="none">
<tbody>
<tr><td valign="top">ADEOS</td><td valign="top">Adaptive Domain Environment for Operating Systems, a patch used by RTAI and Xenomai</td></tr>
<tr><td valign="top">ALU</td><td valign="top">Arithmetic logic unit</td></tr>
<tr><td valign="top">API</td><td valign="top">Application Programming Interface</td></tr> 
<tr><td valign="top">APIC</td><td valign="top">Programmable timer on x86 machines</td></tr>
<tr><td valign="top">AUTOSAR</td><td valign="top">International consortium that defines automotive API</td></tr>
<tr><td valign="top">BCET</td><td valign="top">Best-Case Execution Time</td></tr>
<tr><td valign="top">BFS</td><td valign="top">Breadth First Scheduling</td></tr> 
<tr><td valign="top">BOTS</td><td valign="top">Barcelona OpenMP task suite</td></tr>
<tr><td valign="top">BSD</td><td valign="top">Berkeley software license</td></tr>
<tr><td valign="top">CBS</td><td valign="top">Constant Bandwidth Server, a scheduling algorithm</td></tr>
<tr><td valign="top">CFG</td><td valign="top">Control Flow Graph</td></tr>
<tr><td valign="top">CMP</td><td valign="top">chip multi-processor</td></tr>
<tr><td valign="top">COTS</td><td valign="top">Commercial Off-The-Shelf</td></tr> 
<tr><td valign="top">CPU</td><td valign="top">central processing unit</td></tr>
<tr><td valign="top">CUDA</td><td valign="top">Compute Unified Device Architecture</td></tr>
<tr><td valign="top">DAG</td><td valign="top">Direct Acyclic Graph</td></tr>
<tr><td valign="top">DDR</td><td valign="top">double data rate</td></tr>
<tr><td valign="top">DMA</td><td valign="top">direct memory access (engine)</td></tr>
<tr><td valign="top">DRAM</td><td valign="top">dynamic random-access memory</td></tr>
<tr><td valign="top">DSP</td><td valign="top">digital signal processor</td></tr>
<tr><td valign="top">DSU</td><td valign="top">debug system unit</td></tr>
<tr><td valign="top">EDF</td><td valign="top">Earliest Deadline First Scheduler, a scheduling algorithm</td></tr>
<tr><td valign="top">ELF</td><td valign="top">binary format that contain executables</td></tr>
<tr><td valign="top">eTDG</td><td valign="top">Extended Task Dependency Graph</td></tr>
<tr><td valign="top">EVT</td><td valign="top">Extreme Value Theory</td></tr>
<tr><td valign="top">FIFO</td><td valign="top">First-In First-Out</td></tr>
<tr><td valign="top">FLOPS</td><td valign="top">floating-point operations per second</td></tr>
<tr><td valign="top">FPGA</td><td valign="top">Field-Programmable Gate Array</td></tr>
<tr><td valign="top">GPGPU</td><td valign="top">General Purpose Graphics Processing Unit</td></tr>
<tr><td valign="top">GPL</td><td valign="top">General Public License</td></tr>
<tr><td valign="top">GPOS</td><td valign="top">General purpose Operating System</td></tr>
<tr><td valign="top">GPU</td><td valign="top">Graphics Processing Unit</td></tr>
<tr><td valign="top">GRUB</td><td valign="top">Greedy Reclamation of Unused Bandwidth, a scheduling algorithm</td></tr>
<tr><td valign="top">HAL</td><td valign="top">Hardware Abstraction Layer</td></tr>
<tr><td valign="top">HMI</td><td valign="top">Human Machine Interface</td></tr>
<tr><td valign="top">HPC</td><td valign="top">High Performance Computing</td></tr>
<tr><td valign="top">HRT</td><td valign="top">High Resolution Timers</td></tr>
<tr><td valign="top">ID</td><td valign="top">Identifier</td></tr>
<tr><td valign="top">IEC</td><td valign="top">International Electrotechnical Commission</td></tr>
<tr><td valign="top">IG</td><td valign="top">Interference Generator</td></tr>
<tr><td valign="top">IID</td><td valign="top">Independent and Identically Distributed</td></tr>
<tr><td valign="top">ILP</td><td valign="top">instruction-level parallelism</td></tr>
<tr><td valign="top">IRQ</td><td valign="top">Hardware Interrupt</td></tr> 
<tr><td valign="top">ISA</td><td valign="top">Instruction Set Architecture</td></tr>
<tr><td valign="top">LHCLarge</td><td valign="top">Hadron Collider</td></tr>
<tr><td valign="top">LIFO</td><td valign="top">Last-In First-Out</td></tr>
<tr><td valign="top">LL-RTE</td><td valign="top">Low-level runtime environment</td></tr>
<tr><td valign="top">LRU</td><td valign="top">Least recently used</td></tr>
<tr><td valign="top">MBPTA</td><td valign="top">Measurement-Based Probabilistic Timing Analysis</td></tr>
<tr><td valign="top">MCU</td><td valign="top">Microcontroller Unit</td></tr>
<tr><td valign="top">MEET</td><td valign="top">Maximum Extrinsic Execution Time</td></tr>
<tr><td valign="top">MIET</td><td valign="top">Maximum Intrinsic Execution Time</td></tr>
<tr><td valign="top">MPPA</td><td valign="top">Multi Purpose Processor Array</td></tr> 
<tr><td valign="top">NOC</td><td valign="top">network-on-chip</td></tr>
<tr><td valign="top">NOP</td><td valign="top">No Operation</td></tr>
<tr><td valign="top">NUCA</td><td valign="top">non-uniform cache architecture</td></tr>
<tr><td valign="top">OpenCL</td><td valign="top">Open Computing Language</td></tr>
<tr><td valign="top">OpenMP</td><td valign="top">Open multi processing (programming model)</td></tr>
<tr><td valign="top">OpenMP</td><td valign="top">DAGOpenMP Direct Acyclic Graph</td></tr>
<tr><td valign="top">OS</td><td valign="top">Operating system</td></tr> 
<tr><td valign="top">PCFG</td><td valign="top">Parallel Control Flow Graph</td></tr>
<tr><td valign="top">PCIe</td><td valign="top">peripheral component interconnect express</td></tr>
<tr><td valign="top">PE</td><td valign="top">processing element</td></tr>
<tr><td valign="top">PLRU</td><td valign="top">Pseudo-LRU</td></tr>
<tr><td valign="top">POSIX</td><td valign="top">Portable Operating System Interface for UNIX</td></tr>
<tr><td valign="top">PPC</td><td valign="top">PowerPC</td></tr>
<tr><td valign="top">P-SOCRATES</td><td valign="top">Parallel Software Framework for Time-Critical Many-core Systems</td></tr>
<tr><td valign="top">pWCET</td><td valign="top">Probabilistic Worst-Case Execution Time</td></tr>
<tr><td valign="top">RCU</td><td valign="top">Linux Read-Copy-Update technique</td></tr> 
<tr><td valign="top">RM</td><td valign="top">Resource manager</td></tr>
<tr><td valign="top">RT</td><td valign="top">Real time</td></tr>
<tr><td valign="top">RT</td><td valign="top">task Real-Time task</td></tr>
<tr><td valign="top">RTE</td><td valign="top">Runtime environment</td></tr>
<tr><td valign="top">RTOS</td><td valign="top">Real time operative system</td></tr>
<tr><td valign="top">SDK</td><td valign="top">Software Development Kit</td></tr>
<tr><td valign="top">SMP</td><td valign="top">Symmetric Multi Processor</td></tr>
<tr><td valign="top">SMT</td><td valign="top">simultaneous multi-threading</td></tr>
<tr><td valign="top">SoC</td><td valign="top">System-on-Chip</td></tr>
<tr><td valign="top">SOM</td><td valign="top">system-on-module</td></tr>
<tr><td valign="top">SP</td><td valign="top">Stack pointer</td></tr>
<tr><td valign="top">TBB</td><td valign="top">Thread Building Blocks</td></tr>
<tr><td valign="top">TC</td><td valign="top">Task context</td></tr>
<tr><td valign="top">TDG</td><td valign="top">Task Dependency Graph</td></tr>
<tr><td valign="top">TDMA</td><td valign="top">Time Division Multiple Access</td></tr>
<tr><td valign="top">TLB</td><td valign="top">translation lookaside buffer</td></tr>
<tr><td valign="top">TLP</td><td valign="top">Thread-Level Parallelism</td></tr>
<tr><td valign="top">TSC</td><td valign="top">Task Scheduling Constraint</td></tr>
<tr><td valign="top">TSP</td><td valign="top">Task Scheduling Point</td></tr>
<tr><td valign="top">VLIW</td><td valign="top">Very Large Instruction Word</td></tr>
<tr><td valign="top">VPU</td><td valign="top">vector processing unit</td></tr>
<tr><td valign="top">WCET</td><td valign="top">Worst Case Execution Time</td></tr>
<tr><td valign="top">WFS</td><td valign="top">Work First Scheduling</td></tr> 
</tbody>
</table>
</table-wrap>
</preface>
<preface class="preface" id="preface07">
<title>Contents</title>
<table-wrap position="float">
<table cellspacing="5" cellpadding="5" frame="none" rules="none">
<tbody>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch01">1 Introduction</link></emphasis><?lb?>Lu&#x000ED;s Miguel Pinho, Eduardo Qui&#x000F1;ones, Marko Bertogna, Andrea Marongiu, Vincent N&#x000E9;lis, Paolo Gai and Juan Sancho</td><td valign="top" align="left" width="20%"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C1.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch02">2 Manycore Platforms</link></emphasis><?lb?>Andrea Marongiu, Vincent N&#x000E9;lis and Patrick Meumeu Yomsi</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C2.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch03">3 Predictable Parallel Programming with OpenMP</link></emphasis><?lb?>Maria A. Serrano, Sara Royuela, Andrea Marongiu and Eduardo Qui&#x000F1;ones</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C3.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch04">4 Mapping, Scheduling, and Schedulability Analysis</link></emphasis><?lb?>Paolo Burgio, Marko Bertogna, Alessandra Melani, Eduardo Qui&#x000F1;ones and Maria A. Serrano</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C4.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch05">5 Timing Analysis Methodology</link></emphasis><?lb?>Vincent N&#x000E9;lis, Patrick Meumeu Yomsi and Lu&#x000ED;s Miguel Pinho</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C5.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch06">6 OpenMP Runtime</link></emphasis><?lb?>Andrea Marongiu, Giuseppe Tagliavini and Eduardo Qui&#x000F1;ones</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C6.pdf">Download As PDF</ulink></td></tr>
<tr><td valign="top"><emphasis role="strong"><link linkend="ch07">7 Embedded Operating Systems</link></emphasis><?lb?>Claudio Scordino, Errico Guidieri, Bruno Morelli, Andrea Marongiu, Giuseppe Tagliavini and Paolo Gai</td><td valign="top" align="left"><ulink url="https://www.riverpublishers.com/pdf/ebook/chapter/RP_9788793609624C7.pdf">Download As PDF</ulink></td></tr>
</tbody>
</table>
</table-wrap>
</preface>
<chapter class="chapter" id="ch01" label="1" xreflabel="1">
<title>Introduction</title>
<para><emphasis role="strong">Lu&#x000ED;s Miguel Pinho<superscript>1</superscript>, Eduardo Qui&#x000F1;ones<superscript><emphasis role="strong">2</emphasis></superscript>, Marko Bertogna<superscript><emphasis role="strong">3</emphasis></superscript>, Andrea Marongiu<superscript><emphasis role="strong">4</emphasis></superscript>, Vincent N&#x000E9;lis<superscript><emphasis role="strong">1</emphasis></superscript>, Paolo Gai<superscript><emphasis role="strong">5</emphasis></superscript> and Juan Sancho<superscript><emphasis role="strong">6</emphasis></superscript></emphasis></para>
<para><superscript>1</superscript>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</para>
<para><superscript>2</superscript>Barcelona Supercomputing Center (BSC), Spain</para>
<para><superscript>3</superscript>University of Modena and Reggio Emilia, Italy</para>
<para><superscript>4</superscript>Swiss Federal Institute of Technology in Zurich (ETHZ), Switzerland; and University of Bologna, Italy</para>
<para><superscript>5</superscript>Evidence SRL, Italy</para>
<para><superscript>6</superscript>ATOS, Spain</para>
<para>This chapter provides an overview of the book theme, motivating the need for high-performance and time-predictable embedded computing. It describes the challenges introduced by the need for time-predictability on the one hand, and high-performance on the other, discussing on a high level how these contradictory requirements can be simultaneously supported.</para>
<section class="lev1" id="sec1-1">
<title>1.1 Introduction</title>
<para>High-performance computing has been for a long time the realm of a specific community within academia and specialized industries; in particular those targeting demanding analytics and simulations applications that require processing massive amounts of data. In a similar way, embedded computing has also focused mainly on specific systems with specialized and fixed functionalities and for which timing requirements were considered as much more important than performance requirements. However, with the ever-increasing availability of more powerful processing platforms, alongside affordable and scalable software solutions, both high-performance and embedded computing are extending to other sectors and application domains.</para>
<para>The demand for increased computational performance is currently widespread and is even more challenging when large amounts of data need to be processed, from multiple data sources, with guaranteed processing response times. Although many systems focus on performance and handling large volumes of streaming data (with throughput and latency requirements), many application domains require real-time behavior [1&#x02013;6] and challenge the computing capability of current technologies. Some examples are:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>In cyber-physical systems, ranging from automotive and aircrafts, to smart grids and traffic management, computing systems are embedded in a physical environment and their behavior obeys the technical rules dictated by this environment. Typically, they have to cope with the timing requirements imposed by the embedding domain. In the Large Hadron Collider (LHC) in CERN, beam collisions occur every 25 ns, which produce up to 40 million events per second. All these events are pipelined with the objective of distinguishing between interesting and non-interesting events to reduce the number of events to be processed to a few hundreds [7]. Similarly, bridges are monitored in real-time [8] with information collected from more than 10,000 sensors processed every 8 ms, managing responses to natural disasters, maintaining bridge structure, and estimating the extent of structural fatigue. Another interesting application is in intelligent transportation systems, where systems are developed to allow for fuel consumption reduction of railway systems, managing throttle positions, elaborating big amounts of data and sensor information, such as train horsepower, weight, prevailing wind, weather, traffic, etc. [9].</para></listitem>
<listitem>
<para>In the banking/financial markets, computing systems process large amounts of real-time stock information in order to detect time-dependent patterns, automatically triggering operations in a very specific and tight timeframe when some pre-defined patterns occur. Automated algorithmic trading programs now buy and sell millions of dollars of shares time-sliced into orders separated by 1 ms. Reducing the latency by 1 ms can be worth up to $100 million a year to a leading trading house. The aim is to cut microseconds off the latency in which these systems can reach to momentary variations in share prices [10].</para></listitem>
<listitem>
<para>In industry, computing systems monitor business processes based on the capability to understand and process real-time sensor data from the factory-floor and throughout the whole value chain, with Radio Frequency Identification (RFID) components in order to optimize both the production and logistics processes [11].</para></listitem>
</itemizedlist>
<para>The underlying commonality of the systems described above is that they are time-critical (whether business-critical or mission-critical, it is necessary to fulfill specific timing requirements) and with high-performance requirements. In other words, for such systems, the correctness of the result is dependent on both performance and timing requirements, and meeting those is critical to the functioning of the system. In this context, it is essential to guarantee the timing predictability of the performed computations, meaning that arguments and analyses are needed to be able to make arguments of correctness, e.g., performing the required computations within well-specified bounds.</para>
<section class="lev2" id="sec1-1-1">
<title>1.1.1 The Convergence of High-performance and Embedded Computing Domains</title>
<para>Until now, trends in high-performance and embedded computing domains have been running in opposite directions. On one side, high-performance computing (HPC) systems are traditionally designed to make the common case as fast as possible, without concerning themselves with the timing behavior (in terms of execution time) of the <emphasis>not-so-often cases</emphasis>. As a result, the techniques developed for HPC are based on complex hardware and software structures that make any reliable timing bound almost impossible to derive. On the other side, real-time embedded systems are typically designed to provide energy-efficient and predictable solutions, without heavy performance requirements. Instead of <emphasis>fast</emphasis> response times, they aim at having <emphasis>deterministically bounded response times</emphasis>, in order to guarantee that deadlines are met. For this reason, these systems are typically based on simple hardware architectures, using fixed-function hardware accelerators that are strongly coupled with the application domain.</para>
<para>In the last years, the above design choices are being questioned by the irruption of multi-core processors in both computing markets. The huge computational necessities to satisfy the performance requirements of HPC systems and the related exponential increments of power requirements (typically referred to as the power wall) exceeded the technological limits of classic single-core architectures. For these reasons, the main hardware manufacturers are offering an increasing number of computing platforms integrating multiple cores within a chip, contributing to an unprecedented phenomenon sometimes referred to as &#x0201C;the multi-core revolution.&#x0201D; Multi-core processors provide better energy efficiency and performance-per-cost ratio, while improving application performance by exploiting thread-level parallelism (TLP). Applications are split into multiple tasks that run in parallel on different cores, extending to the <emphasis>multi-core</emphasis> system level an important challenge already faced by HPC designers at <emphasis>multi-processor</emphasis> system level: parallelization.</para>
<para>In the embedded systems domain, the necessity to develop more flexible and powerful systems (e.g., from fixed-function phones to smart phones and tablets) have pushed the embedded market in the same direction. That is, multi-cores are increasingly considered as the solution to cope with performance and cost requirements [12], as they allow scheduling multiple application services on the same processor, hence maximizing the hardware utilization while reducing cost, size, weight, and power requirements. However, real-time embedded applications with time-criticality requirements are still executed on simple architectures that are able to guarantee a predictable execution pattern while avoiding the appearance of timing anomalies [13]. This makes real-time embedded platforms still relying on either single-core or simple multi-core CPUs, integrated with fix-function hardware accelerators into the same chip: the so-called System-on-Chip (SoC).</para>
<para>The needs for energy-efficiency (in the HPC domain) and for flexibility (in the embedded computing domain), coming along with Moore&#x02019;s law, greedy demand for performance, and the advancements in the semiconductor technology, have progressively paved the way for the introduction of &#x0201C;<emphasis>many-core</emphasis>&#x0201D; systems, i.e., multi-core chips containing a high number of cores (tens to hundreds) in both domains. Examples of many-core architectures are described in the next chapter.</para>
<para>The introduction of many-core systems has set up an interesting trend wherein both the HPC and the real-time embedded domains converge towards similar objectives and requirements. Many-core computing fabrics are being integrated with general-purpose multi-core processors to provide a heterogeneous architectural harness that eases the integration of previously hardwired accelerators into more flexible software solutions. In recent years, the HPC computing domain has seen the emergence of accelerated heterogeneous architectures, most notably multi-core processors integrated with General Purpose Graphic Processing Units (GPGPU), because GPGPUs are a flexible and programmable accelerator for data parallel computations. Similarly, in the real-time embedded domain, the Kalray Multi-Purpose Processor Array (MPPA), which includes clusters of quad-core CPUs coupled with many-core computing clusters. In both cases, the many-core fabric acts as a programmable accelerator. More recently, the Field-Programmable Gate Array (FPGA) has been used as a flexible accelerator fabric, complementing the above.</para>
<para>In this current trend, challenges that were previously specific to each computing domain, start to be common to both domains (including energy-efficiency, parallelization, compilation, and software programming) and are magnified by the ubiquity of many-cores and heterogeneity across the whole computing spectrum. In that context, cross-fertilization of expertise from both computing domains is <emphasis>mandatory</emphasis>.</para>
</section>
<section class="lev2" id="sec1-1-2">
<title>1.1.2 Parallelization Challenge</title>
<para>Needless to say that many industries with both high-performance and real-time requirements are eager to benefit from the immense computing capabilities offered by these new many-core embedded designs. However, these industries are also highly unprepared for shifting their earlier system designs to cope with this new technology, mainly because such a shift requires adapting the applications, operating systems, and programming models in order to exploit the capabilities of many-core embedded computing systems. On one hand, neither have many-core embedded processors, such as the MPPA, been designed to be used in the HPC domain, nor have HPC techniques been designed to apply embedded technology. On the other hand, real-time methods to determine the timing behavior of an embedded system are not prepared to be directly applied to the HPC domain and these platforms, leading to a number of significant challenges.</para>
<para>On one side, different parallel programming models and multiprocessor operating systems have been proposed and are increasingly being adopted in today&#x02019;s HPC computing systems. In recent years, the emergence of accelerated heterogeneous architectures such as GPGPUs have introduced parallel programming models such as OpenCL [14], the currently dominant open standard for parallel programming of heterogeneous systems, or CUDA [15], the dominant proprietary framework of NVIDIA. Unfortunately, they are not easily applicable to systems with real-time requirements, since, by nature, many-core architectures are designed to integrate as much functionality as possible into a single chip. Hence, they inherently share out as many resources as possible amongst the cores, which heavily impacts the ability to providing timing guarantees.</para>
<para>On the other side, the embedded computing domain world has always seen plenty of application-specific accelerators with custom architectures, manually tuning applications to achieve predictable performance. Such types of solutions have limited flexibility, complicating the development of embedded systems. Commercial off-the-shelf (COTS) components based on many-core architectures are likely to dominate the embedded computing market in the near future, even if complemented with custom function-specific accelerators. As a result, migrating real-time applications to many-core execution models with predictable performance requires a complete redesign of current software architectures. Real-time embedded application developers will therefore either need to adapt their programming practices and operating systems to future many-core components, or they will need to content themselves with stagnating execution speeds and reduced functionalities, relegated to niche markets using obsolete hardware components.</para>
<para>This new trend in the manufacturing technology and the industrial need for enhanced computing capabilities and flexible heterogeneous programming solutions of accelerators for predictable parallel computations bring to the forefront important challenges for which solutions are urgently needed. This book outlines how to bring together next-generation many-core accelerators from the embedded computing domain with the programmability of many-core accelerators from the HPC computing domain, supporting this with real-time methodologies to provide time predictability and high-performance.</para>
</section>
</section>
<section class="lev1" id="sec1-2">
<title>1.2 The P-SOCRATES Project</title>
<para>The work described in this book was performed in the scope of the European project P-SOCRATES (Parallel Software Framework for Time-Critical Many-core Systems) <footnote id="fn1_1" label="1"> <para>htttp://www.p-socrates.eu</para></footnote>, funded under the FP7 framework program of the European Commission. The project, finished in December 2016, aimed to allow applications with high-performance and real-time requirements to fully exploit the huge performance opportunities brought by the most advanced COTS many-core embedded processors, whilst ensuring predictable performance of applications (<link linkend="F1-1">Figure <xref linkend="F1-1" remap="1.1"/></link>). The project consortium included Instituto Superior de Engenharia do Porto (coordinator), Portugal, the Barcelona Supercomputing Centre, Spain, the University of Modena and Reggio Emilia, Italy, the Swiss Federal Institute of Technology Zurich, Switzerland, Evidence SRL, Italy, Active Technologies SRL, Italy and ATOS, Spain.</para>
<fig id="F1-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F1-1">Figure <xref linkend="F1-1" remap="1.1"/></link></label>
<caption><para>P-SOCRATES Global perspective.</para></caption>
<graphic xlink:href="graphics/ch01_fig1_1a.jpg"/>
</fig>
<para>P-SOCRATES focused on combining techniques from different domains: the newest high-performance software techniques for exploiting task parallelism, the most advanced mapping and scheduling methodologies and timing and schedulability analysis techniques used in real-time embedded systems, and the low-energy many-core platforms of the embedded domain. This allowed taking important steps towards the convergence of HPC and real-time and embedded domains (<link linkend="F1-2">Figure <xref linkend="F1-2" remap="1.2"/></link>), providing predictable performance to HPC systems and increasing performance of real-time embedded systems.<emphasis role="strong"></emphasis></para>
<fig id="F1-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F1-2">Figure <xref linkend="F1-2" remap="1.2"/></link></label>
<caption><para>P-SOCRATES combines high-performance parallel programming models, high-end embedded many-core platforms and real-time systems technology.</para></caption>
<graphic xlink:href="graphics/ch01_fig1_2.jpg"/>
</fig>
<para>P-SOCRATES developed a complete and coherent software system stack, able to bridge the gap between the application design with both high-performance and real-time requirements, and the hardware platform, a many-core embedded processor. The project provided a <emphasis>new framework</emphasis> to combine real-time embedded mapping and scheduling techniques with high-performance parallel programming models and associated tools, able to express parallelization of applications. The programming model used was based on the state-of-the-art OpenMP specification.</para>
<para>The software stack (shown in <link linkend="F1-3">Figure <xref linkend="F1-3" remap="1.3"/></link>) is able to extract a task-dependency graph from the application, statically or dynamically mapping these tasks to the threads of the operating system, which then dynamically schedules them on the many-core platform.</para>
<fig id="F1-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F1-3">Figure <xref linkend="F1-3" remap="1.3"/></link></label>
<caption><para>Vertical stack of application decomposition.</para></caption>
<graphic xlink:href="graphics/ch01_fig1_3.jpg"/>
</fig>
</section>
<section class="lev1" id="sec1-3">
<title>1.3 Challenges Addressed in This Book</title>
<section class="lev2" id="sec1-3-1">
<title>1.3.1 Compiler Analysis of Parallel Programs</title>
<para>In order to enable predictable parallel performance to be analyzed, it is required that the application parallel graph is known, with control- and data-flow information needed for the analysis of the timing behavior of the parallel program. The extraction of this information should be as automatic as possible, to release the programmer from the burden of needing to understand the exact hardware details.</para>
<para><link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link> addresses this challenge by presenting how this information can be obtained from the OpenMP tasking model, and how this information can be used to derive the timing properties of an application parallelized using this model.</para>
</section>
<section class="lev2" id="sec1-3-2">
<title>1.3.2 Predictable Scheduling of Parallel Tasks on Many-core Systems</title>
<para>To be able to derive guarantees on the correct timing execution of parallel programs, it is required to provide appropriate mapping and scheduling algorithms of parallel computation in many-core platforms, together with deriving the associated offine analysis that enable determining if applications will meet their deadlines.</para>
<para>The challenge of real-time scheduling and schedulability analysis of parallel code is discussed in <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link>, which provides the substantial advances that the project has performed in the real-time scheduling and schedulability analysis of parallel graphs, using different scheduling models.</para>
</section>
<section class="lev2" id="sec1-3-3">
<title>1.3.3 Methodology for Measurement-based Timing Analysis</title>
<para>The use of multi- and many-core platforms considerably challenges approaches for real-time timing analysis, required to determine worst-case execution time of the application code. In fact, the analysis of code execution time is considerably complex due to the interaction and conflicts between the multiple cores utilizing the same hardware resources (e.g., bus, memory, network).</para>
<para><link linkend="ch05">Chapter <xref linkend="ch5" remap="5"/></link> investigates the different available methods to perform this timing analysis in a many-core setting. After weighing the advantages and disadvantages of each technique, a new methodology is presented based on runtime measurements to derive worst-case estimates.</para>
</section>
<section class="lev2" id="sec1-3-4">
<title>1.3.4 Optimized OpenMP Tasking Runtime System</title>
<para>The methodology presented in Chapters 3 to 5 of this book relies on the parallel computing abstraction provided by the OpenMP tasking model, and its conceptual similarities to the Direct Acyclic Graph (DAG) model, to achieve predictable task scheduling, requiring an efficient runtime support. However, a space- and performance-efficient design of a tasking run-time environment targeting a many-core system-on-chip is a challenging task, as embedded parallel applications typically exhibit very fine-grained parallelisms.</para>
<para>For that purpose, <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link> presents the design and implementation of an OpenMP tasking run-time environment with very low time and space overheads, which is able to support the approach of the book.</para>
</section>
<section class="lev2" id="sec1-3-5">
<title>1.3.5 Real-time Operating Systems</title>
<para>The run-time environment of <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link> requires the underlying support of a Real-Time Operating System (RTOS) for many-core architectures. This operating system needs to both be able to execute multi-threaded applications in multiple cores, and also efficiently support a limited pre-emptive model, where threads are only pre-empted at the boundaries of OpenMP tasks.</para>
<para><link linkend="ch07">Chapter <xref linkend="ch7" remap="7"/></link> presents the re-design and re-implementation of the ERIKA Enterprise RTOS, aiming at an efficient execution on this kind of platforms. The new version of the RTOS allows us to share a single binary kernel image across several cores of the platform, reducing the overall memory consumption, and includes the new limited pre-emptive model.</para>
</section>
</section>
<section class="lev1" id="sec1-4">
<title>1.4 The UpScale SDK</title>
<para>An outcome of the P-SOCRATES project was a complete and coherent software framework for applications with high-performance and real-time requirements in COTS many-core embedded processors. This software framework was publicly released under the brand of the UpScale SDK (Software Development Kit) <footnote id="fn1_2" label="2"> <para>http://www.upscale-sdk.com</para></footnote>. The UpScale SDK includes the tools to manage the application compilation process, its timing analysis and its execution (<link linkend="F1-4">Figure <xref linkend="F1-4" remap="1.4"/></link>):</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis>Compiler flow</emphasis>. This flow has a twofold objective: (i) to guide the process to generate the binary that will execute on the many-core architecture and (ii) to generate the application DAG used for the timing analysis and run-time components.</para></listitem>
<listitem>
<para><emphasis>Analysis flow</emphasis>. This flow is in charge of deriving timing guarantees of the parallel execution considering execution time traces of the application running on the many-core platform and incorporated in the DAG. Timing guarantees are derived by means of execution time bounds and a static scheduler or dynamic scheduler supported with response-time analysis.</para></listitem>
<listitem>
<para><emphasis>Execution stack</emphasis>. These two components are in charge of orchestrating the parallel execution of the application in a time-predictable manner, based on the DAG.</para></listitem>
</itemizedlist>
<fig id="F1-4" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F1-4">Figure <xref linkend="F1-4" remap="1.4"/></link></label>
<caption><para>The UpScale SDK.</para></caption>
<graphic xlink:href="graphics/ch01_fig1_4.jpg"/>
</fig>
</section>
<section class="lev1" id="sec1-5">
<title>1.5 Summary</title>
<para>Providing high performance while meeting predictability requirements of real-time applications is a challenging task, which requires new techniques and tools at most if not all levels of the design flow and execution stack. This book presents the work which was done within the P-SOCRATES project to address these challenges, presenting solutions for deriving control- and data-flow graph of OpenMP parallel programs using the tasking model, algorithms for mapping and scheduling the OpenMP tasks into many-core platforms, and methods to perform both timing and schedulability analysis. The book also describes solutions for the runtime execution stack for real-time parallel computation, both at the level of the OpenMP runtime, as well as within real-time operating systems.</para>
</section>
<section class="lev1" id="sec1-6">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Magid, Y., Adi, A., Barnea, M., Botzer, D., Rabinovich, E., &#x0201C;Application generation framework for real-time complex event processing,&#x0201D; <emphasis>32nd Annual IEEE International Computer Software and Applications (COMPSAC)</emphasis>, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Magid%2C+Y%2E%2C+Adi%2C+A%2E%2C+Barnea%2C+M%2E%2C+Botzer%2C+D%2E%2C+Rabinovich%2C+E%2E%2C+%22Application+generation+framework+for+real-time+complex+event+processing%2C%22+32nd+Annual+IEEE+International+Computer+Software+and+Applications+%28COMPSAC%29%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Anicic, D., Rudolph, S., Fodor, P., Stojanovic, N., &#x0201C;Stream reasoning and complex event processing in ETALIS,&#x0201D; <emphasis>Semantic Web 1</emphasis>, 2009, IOS Press, pp. 1&#x02013;5. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Anicic%2C+D%2E%2C+Rudolph%2C+S%2E%2C+Fodor%2C+P%2E%2C+Stojanovic%2C+N%2E%2C+%22Stream+reasoning+and+complex+event+processing+in+ETALIS%2C%22+Semantic+Web+1%2C+2009%2C+IOS+Press%2C+pp%2E+1-5%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Luckham, D. C., &#x0201C;<emphasis>Event Processing for Business: Organizing the Real-Time Enterprise,</emphasis>&#x0201D; John Wiley and Sons, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Luckham%2C+D%2E+C%2E%2C+%22Event+Processing+for+Business%3A+Organizing+the+Real-Time+Enterprise%2C%22+John+Wiley+and+Sons%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Palmer, M., &#x0201C;<emphasis>Real-Time Big Data and the 11 Principles of Modern Surveillance Systems</emphasis>,&#x0201D; <ulink url="http://streambase.typepad.com/streambase_stream_process/2011/07/in-his-tabbforum-article-dave-tolladay-eloquently-argues-that-real-time-surveillance-is-crucial-in-todays-high-frequency-t.html">http://streambase.typepad.com/streambase_stream_process/2011/07/in-his-tabbforum-article-dave-tolladay-eloquently-argues-that-real-time-surveillance-is-crucial-in-todays-high-frequency-t.html</ulink>, last accessed February 2018.</para></listitem>
<listitem>
<para>Twentyman, J., &#x0201C;<emphasis>Sensory Perception,</emphasis>&#x0201D; <ulink url="http://www.information-age.com/technology/information-management/1248733/sensory-perception">http://www.information-age.com/technology/information-management/1248733/sensory-perception</ulink>, last accessed February 2018.</para></listitem>
<listitem>
<para>Klein, R., Xie, J., and Usov, A., &#x0201C;Complex events and actions to control cyber-physical systems.&#x0201D; In <emphasis>Proceedings of the 5th ACM International Conference on Distributed Event-Based System (DEBS)</emphasis>, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Klein%2C+R%2E%2C+Xie%2C+J%2E%2C+and+Usov%2C+A%2E%2C+%22Complex+events+and+actions+to+control+cyber-physical+systems%2E%22+In+Proceedings+of+the+5th+ACM+International+Conference+on+Distributed+Event-Based+System+%28DEBS%29%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Shapiro, M., &#x0201C;Supersymmetry, extra dimensions and the origin of mass: exploring the nature of the universe using petaScale data analysis,&#x0201D; <emphasis>Google TechTalk</emphasis>, June 18, 2007.</para></listitem>
<listitem>
<para>&#x0201C;<emphasis>NTT DATA: Staying Ahead of the IT Services Curve With Real-Time Analytics</emphasis>,&#x0201D; <ulink url="https://www.sap.com/sea/documents/2012/10/66e7c78d-357c-0010-82c7-eda71af511fa.html">https://www.sap.com/sea/documents/2012/10/66e7c78d-357c-0010-82c7-eda71af511fa.html</ulink>, last accessed February 2018.</para></listitem>
<listitem>
<para>&#x0201C;<emphasis>SAP Enters Complex-event Processing Market</emphasis>,&#x0201D; <ulink url="http://www.cio.com.au/article/377688/sap_enters_complex-event_processing_market/">http://www.cio.com.au/article/377688/sap_enters_complex-event_processing_market/</ulink>, last accessed February 2018.</para></listitem>
<listitem>
<para>Tieman, R., &#x0201C;Algo trading: the dog that bit its master&#x0201D;, <emphasis>Financial Times</emphasis>, March 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Tieman%2C+R%2E%2C+%22Algo+trading%3A+the+dog+that+bit+its+master%22%2C+Financial+Times%2C+March+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Karim, L., Boulmakoul, A., Lbath, A., &#x0201C;Near real-time big data analytics for NFC-enabled logistics trajectories,&#x0201D; <emphasis>2016 3rd International Conference on Logistics Operations Management (GOL),</emphasis> Fez, 2016, pp. 1&#x02013;7. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Karim%2C+L%2E%2C+Boulmakoul%2C+A%2E%2C+Lbath%2C+A%2E%2C+%22Near+real-time+big+data+analytics+for+NFC-enabled+logistics+trajectories%2C%22+2016+3rd+International+Conference+on+Logistics+Operations+Management+%28GOL%29%2C+Fez%2C+2016%2C+pp%2E+1-7%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Ungerer, T., et. al. &#x0201C;MERASA: Multi-core execution of hard real-time applications supporting analysability,&#x0201D; In <emphasis>the IEEE Micro 2010, Special Issue on European Multicore Processing Projects</emphasis>, Vol. 30, No. 5, October 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Ungerer%2C+T%2E%2C+et%2E+al%2E+%22MERASA%3A+Multi-core+execution+of+hard+real-time+applications+supporting+analysability%2C%22+In+the+IEEE+Micro+2010%2C+Special+Issue+on+European+Multicore+Processing+Projects%2C+Vol%2E+30%2C+No%2E+5%2C+October+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Lundqvist, T., Stenstrom, P., &#x0201C;Timing anomalies in dynamically scheduled microprocessors.&#x0201D; In <emphasis>IEEE Real-Time Systems Symposium</emphasis>, 1999. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lundqvist%2C+T%2E%2C+Stenstrom%2C+P%2E%2C+%22Timing+anomalies+in+dynamically+scheduled+microprocessors%2E%22+In+IEEE+Real-Time+Systems+Symposium%2C+1999%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>&#x0201C;<emphasis>OpenCL (Open Computing Language)</emphasis>&#x0201D;, <ulink url="http://www.khronos.org/opencl">http://www.khronos.org/opencl</ulink>, last accessed February 2018.</para></listitem>
<listitem>
<para><emphasis>NVIDIA</emphasis>, <ulink url="https://developer.nvidia.com/cuda-zone">https://developer.nvidia.com/cuda-zone</ulink>, last accessed February 2018.</para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch02" label="2" xreflabel="2">
<title>Manycore Platforms</title>
<para><emphasis role="strong">Andrea Marongiu<superscript>1</superscript>, Vincent N&#x000E9;lis<superscript>2</superscript> and Patrick Meumeu Yomsi<superscript>2</superscript></emphasis></para>
<para><superscript>1</superscript>Swiss Federal Institute of Technology in Z&#x000FC;rich (ETHZ), Switzerland; and University of Bologna, Italy<?lb?><superscript>2</superscript>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</para> 
<para>This chapter surveys state-of-the-art manycore platforms. It discusses the historical evolution of computing platforms over the past decades and the technical hurdles that led to the manycore revolution, then presents in details several manycore platforms, outlining (i) the key architectural traits that enable scalability to several tens or hundreds of processing cores and (ii) the shared resources that are responsible for unpredictable timing.</para>
<section class="lev1" id="sec2-1">
<title>2.1 Introduction</title>
<para>Starting from the early 2000s, general-purpose processor manufacturers adopted the chip multiprocessor (CMP) design paradigm [1] to overcome technological &#x0201C;walls.&#x0201D;</para>
<para>Single-core processor designs hit the <emphasis role="strong">power wall</emphasis> around 2004, when the consolidated strategy of scaling down the gate size of integrated circuits &#x02013; reducing the supply voltage and increasing the clock frequency &#x02013; became unfeasible because of excessive power consumption and expensive packaging and cooling solutions [2]. The CMP phisolophy replaces a single, very fast core with multiple cores that cooperate to achieve equivalent performance, but each operating at a lower clock frequency and thus consuming less power.</para>
<para>Over the past 20 years, processor performance has increased at a faster rate than the memory performance [3], which created a gap that is commonly referred to as the <emphasis role="strong">memory wall</emphasis>. Historically, sophisticated multi-level cache hierarchies have been built to implement main memory access latency hiding techniques. As CMPs use lower clock frequencies, the processor&#x02013;memory gap grows at a slower rate, compared to traditional single-core systems. Globally, the traditional latency hiding problem is turned into an increased bandwidth demand, which is easier to address, as the DRAM bandwidth scales much better than its access latency [4].</para>
<para>Single-core designs have traditionally been concerned with the development of techniques to efficiently extract instruction-level parallelism (ILP). However, increasing ILP performance beyond what is achieved today with state-of-the-art techniques has become very difficult [5], which is referred to as the <emphasis role="strong">ILP wall</emphasis>. CMPs solve the problem by shifting the focus to thread-level parallelism (TLP), which is exposed at the parallel programming model level, rather than designing sophisticated hardware to transparently extract ILP from instruction streams.</para>
<para>Finally, the <emphasis role="strong">complexity</emphasis> wall refers to the difficulties encountered by single-core chip manifacturers in designing and verifying increasingly sophisticated out-of-order processors. In the CMP design paradigm, a much simpler processor core is designed once and replicated to scale to the multicore system core count. Design reuse and simplified core complexity obviously significantly reduce the system design and verification.</para>
<para>The trend towards integrating an increasing number of cores in a single chip has continued all over the past decade, which has progressively paved the way for the introduction of manycore systems, i.e., CMPs containing a high number of cores (tens to hundreds). Interestingly, the same type of &#x0201C;revolution&#x0201D; has taken place virtually in every domain, from the high-performance computing (HPC) to the embedded systems (ES). Driven by converging needs for high performance requirements, energy efficiency, and flexibility, the most representative commercial platforms from both domains nowadays feature very similar architectural traits. In particular, core <emphasis>clusterization</emphasis> is the key design paradigm adopted in all these products. A hierarchical processor organization is always employed, where simple processing units are grouped into small-medium sized subsystems (the <emphasis>clusters</emphasis>) and share high-performance local interconnection and memory. Scaling to larger system sizes is enabled by replicating clusters and interconnecting them with a scalable medium like a network-on-chip (NoC).</para>
<para>In the following, we briefly present several manycore platforms, both from the HPC and the ES domains. We discuss the Kalray MPPA-256 at last, and in greater detail, as this is the platform for which the development of the software techniques and the experimental evaluation presented throughout the rest of the book have been conducted.</para>
</section>
<section class="lev1" id="sec2-2">
<title>2.2 Manycore Architectures</title>
<section class="lev2" id="sec2-2-1">
<title>2.2.1 Xeon Phi</title>
<fig id="F2-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-1">Figure <xref linkend="F2-1" remap="2.1"/></link></label>
<caption><para>Knights Landing (KNL) block diagram: (a) the CPU, (b) an example tile, and (c) KNL with Omni-Path Fabric integrated on the CPU package.</para></caption>
<graphic xlink:href="graphics/ch02_xeonphi.jpg"/>
</fig>
<para>Xeon Phi are a series of x86 manycore processors by Intel and meant to accelerate the highly parallel workloads of the HPC world. As such, they are employed in supercomputers, servers, and high-end workstations. The Xeon Phi family of products has its roots in the <emphasis>Larrabee</emphasis> microarchitecture project &#x02013; an attempt to create a manycore accelerator meant as a GPU as well as for general-purpose computing &#x02013; and has recently seen the launch of the Knights Landing (KNL) chip on the marketplace.</para>
<para><link linkend="F2-1">Figure <xref linkend="F2-1" remap="2.1"/></link>a shows the high-level block diagram of the KNL CPU. It comprises 38 physical <emphasis>tiles</emphasis>, of which at most 36 are active (the remaining two tiles are for yield recovery). The structure of a <emphasis>tile</emphasis> is shown in <link linkend="F2-1">Figure <xref linkend="F2-1" remap="2.1"/></link>b. Each <emphasis>tile</emphasis> comprises two cores, two vector processing units (VPUs) per core, and a 1-Mbyte level-2 (L2) cache that is shared between the two cores.</para>
<para>The core is derived from the Intel Atom (based on the Silvermont microarchitecture [6]), but leverages a new two-wide, out-of-order core which includes heavy modifications to incorporate features necessary for HPC workloads [e.g., four threads per core, deeper out-of-order buffers, higher cache bandwidth, new instructions, better reliability, larger translation look-aside buffers (TLBs), and larger caches]. In addition, the new Advanced Vector Extensions instruction set, AVX-512, provides 512-bit-wide vector instructions and more vector registers.</para>
<para>At the top level, a 2D, cache-coherent mesh NoC connects the tiles, memory controllers, I/O controllers, and other agents on the chip.</para>
<para>The mesh supports the MESIF (modified, exclusive, shared, invalid, forward) protocol, which employs a distributed tag directory to keep the L2 caches in all tiles coherent with each other. Each tile contains a caching/home agent that holds a portion of the distributed tag directory and also serves as a connection point between the tile and the mesh.</para>
<para>Knights Landing features two types of memory: (i) multichannel DRAM (MCDRAM) and (ii) double data rate (DDR) memory. MCDRAM is organized as eight devices &#x02013; each featuring 2-Gbyte high-bandwidth banks &#x02013; integrated on-package and connected to the KNL die via a proprietary on-package I/O. The DDR4 is organized as six channels running at up to 2,400 MHz, with three channels on each of two memory controllers.</para>
<para>The two types of memory are presented to users in three memory modes: cache mode, in which MCDRAM is a cache for DDR; flat mode, in which MCDRAM is treated like standard memory in the same address space as DDR; and hybrid mode, in which a portion of MCDRAM is cache and the remainder is flat. KNL supports a total of 36 lanes of PCI express (PCIe) Gen3 for I/O, split into two x16 lanes and one x4 lane.</para>
<para>Moreover, it integrates the Intel Omni-Path Fabric on-package (see <link linkend="F2-1">Figure <xref linkend="F2-1" remap="2.1"/></link>c), which provides two 100-Gbits-per-second ports out of the package.</para>
<para>The typical power (thermal design power) for KNL (including MCDRAM memory) when running a computationally intensive workload is 215 W without the fabric and 230 W with the fabric.</para>
</section>
<section class="lev2" id="sec2-2-2">
<title>2.2.2 Pezy SC</title>
<fig id="F2-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-2">Figure <xref linkend="F2-2" remap="2.2"/></link></label>
<caption><para>PEZY-SC architecture block diagram.</para></caption>
<graphic xlink:href="graphics/ch02_pezysc.jpg"/>
</fig>
<para>PEZY-SC (PEZY Super Computer) [7] is the second generation manycore microprocessor developed by PEZY in 2014, and is widely used as an accelerator for HPC workloads. Compared to the original PEZY-1, the chip contains exactly twice as many cores and incorporates a large amount of cache including 8 MB of L3$. Operating at 733 MHz, the processor is said to have peak performance of 3.0 TFLOPS (single-precision) and 1.5 TFLOPS (double-precision). PEZY-SC was designed using 580 million gates and manufactured on TSMC&#x02019;s 28HPC+ (28 nm process).</para>
<para>In June 2015, PEZY-SC-based supercomputers took all top three spots on the Green500 listing as the three most efficient supercomputers:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis role="strong">Shoubu</emphasis>: 1,181,952 cores, 50.3 kW, 605.624 TFlop/s Linpack Rmax;</para></listitem>
<listitem>
<para><emphasis role="strong">Suiren Blue</emphasis>: 262,656 cores, 40.86 kW, 247.752 TFlop/s Linpack Rmax;</para></listitem>
<listitem>
<para><emphasis role="strong">Suiren</emphasis>: 328,480 cores, 48.90 kW, 271.782 TFlop/s Linpack Rmax.</para></listitem>
</orderedlist>
<para>PEZY-SC contains two ARM926 cores (ARMv5TEJ) along with 1024 simpler RISC cores supporting 8-way SMT for a total of 8,192 threads, as shown in <link linkend="F2-2">Figure <xref linkend="F2-2" remap="2.2"/></link>. The organization of the accelerator cores in PEZY-SC heavily uses clusterization and hierarchy. At the top level, the microprocessor is made of four blocks called &#x0201C;<emphasis>prefectures</emphasis>.&#x0201D; Within a <emphasis>prefecture</emphasis>, 16 smaller blocks called &#x0201C;<emphasis>cities</emphasis>&#x0201D; share 2 MB of L3$. Each <emphasis>city</emphasis> is composed of 64 KB of shared L2$, a number of special function units and four smaller blocks called &#x0201C;<emphasis>villages</emphasis>.&#x0201D; Inside a <emphasis>village</emphasis> there are four execution units and every two such execution units share 2 KB of L1D$.</para>
<para>The chip has a peak power dissipation of 100 W with a typical power consumption of 70 W which consists of 10 W leakage + 60 W dynamic.</para>
</section>
<section class="lev2" id="sec2-2-3">
<title>2.2.3 NVIDIA Tegra X1</title>
<fig id="F2-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-3">Figure <xref linkend="F2-3" remap="2.3"/></link></label>
<caption><para>NVIDIA Tegra X1 block diagram.</para></caption>
<graphic xlink:href="graphics/ch02_tegraX1.jpg"/>
</fig>
<para>The NVIDIA Tegra X1 [8] is a hybrid System on Module (SoM) featured in the NVIDIA Jetson Development boards. As a mobile processor, the Tegra X1 is meant for the high-end ES markets, and is the first system to feature a chip powerful enough to sustain the visual computing load for autonomous and assisted driving applications.</para>
<para>As shown in <link linkend="F2-3">Figure <xref linkend="F2-3" remap="2.3"/></link>, the X1 CPU complex consists of a big LITTLE architecture, featuring quad-core 1.9 GHz ARM Cortex-A57 processor (48 KB I-cache + 32 kB D-cache L1 per core, 2 MB L2 cache common to all cores), plus quad-core ARM Cortex A53 processor. A single CPU core can utilize the maximum bandwidth available for the whole CPU complex, which amounts to almost 4.5 GB/s for sequential read operations.</para>
<para>The iGPU is a second-generation Maxwell &#x0201C;GM20b&#x0201D; architecture, with 256 CUDA cores grouped in two Streaming Multi-processors (SMs) (the &#x0201C;<emphasis>clusters</emphasis>&#x0201D;) sharing a 256 KB L2 (last-level) cache. The compute pipeline of an NVIDIA GPU includes engines responsible for computations (Execution Engine, EE) and engines responsible for high bandwidth memory transfers (Copy Engine, CE). The EE and CE can access central memory with a maximum bandwidth close to 20 GB/s, which can saturate the whole DRAM bandwidth. Indeed, the system DRAM consists of 4 GB of LPDDR4 64 bit SDRAM working at (maximum) 1.6 GHz, reaching a peak ideal bandwidth of 25.6 GB/s.</para>
<para>Despite the high performance capabilities of the SoC (peak performance 1 TFlops single precision), the Tegra X1 features a very contained power envelope, drawing 6&#x02013;15 W.</para>
</section>
<section class="lev2" id="sec2-2-4">
<title>2.2.4 Tilera Tile</title>
<fig id="F2-4" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-4">Figure <xref linkend="F2-4" remap="2.4"/></link></label>
<caption><para>Tilera <emphasis>Tile</emphasis> architectural template.</para></caption>
<graphic xlink:href="graphics/ch02_tilera.jpg"/>
</fig>
<para>The <emphasis>Tile</emphasis> architecture has its roots in the RAW research processor developed at MIT [9] and later commercialized by Tilera, a start-up founded by the original research group. Chips from the second generation are expected to scale up to 100 cores based on the MIPS ISA and running at 1.5 GHz.</para>
<para>The <emphasis>Tile</emphasis> architecture is among the first examples of a cluster-based many-core, featuring <emphasis>ad-hoc</emphasis> on-chip interconnect and cache architecture. The architectural template is shown in <link linkend="F2-4">Figure <xref linkend="F2-4" remap="2.4"/></link>. The chip is architected as a 2D array of <emphasis>tiles</emphasis> (the <emphasis>clusters</emphasis>), interconnected via a mesh-based NoC. Each tile contains a single processor core, with local L1 (64 KB) and a portion (256 KB) of the distributed L2 cache. Overall, the L2 cache segments behave as a non-uniformly addressed cache (NUCA), using a directory-based coherence mechanism and the concept of <emphasis>home tile</emphasis> (the tile that holds the master copy) for cached data. The NUCA design makes cache access latency variable according to the distance between tiles, but enables an efficient (space- and power-wise) logical view to the programmer: a large on-chip cache to which all cores are connected. Each tile also features an interconnect switch that connects it to the neighboring tiles, which allows for a simplified interconnect design (essentially, a switched network with very short wires connecting neighboring tiles linked through the tile-local switch).</para>
<para>The NoC &#x02013; called <emphasis>iMesh</emphasis> by Tilera &#x02013; actually consists of five different networks, used for various purposes:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Application process communication (UDN),</para></listitem>
<listitem>
<para>I/O communication (IDN),</para></listitem>
<listitem>
<para>Memory communication (MDN),</para></listitem>
<listitem>
<para>Cache coherency (TDN),</para></listitem>
<listitem>
<para>Static, channelized communication (STN).</para></listitem>
</itemizedlist>
<para>The latency of the data transfers on the network is 1&#x02013;2 cycles/tile, depending on whether there&#x02019;s a direction change or not at the tile.</para>
<para>The <emphasis>TileDirect</emphasis> technology allows data received over the external interfaces to be placed directly into the tile-local memory, thus bypassing the external DDR memory and reducing memory traffic.</para>
<para>The power budget of the <emphasis>Tile</emphasis> processors is under 60 W.</para>
<fig id="F2-5" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-5">Figure <xref linkend="F2-5" remap="2.5"/></link></label>
<caption><para>STMicroelectronics STHORM heterogeneous system.</para></caption>
<graphic xlink:href="graphics/ch02_sthorm.jpg"/>
</fig>
</section>
<section class="lev2" id="sec2-2-5">
<title>2.2.5 STMicroelectronics STHORM</title>
<para>STHORM is a heterogeneous, manycore-based system from STMicroelectronics [10], with an operating frequency ranging up to 600 MHz.</para>
<para>The STHORM architecture is organized as a fabric of multi-core <emphasis>clusters</emphasis>, as shown in <link linkend="F2-5">Figure <xref linkend="F2-5" remap="2.5"/></link>. Each cluster contains 16 STxP70 <emphasis>Processing Elements</emphasis> (PEs), each of which has a 32-bit dual-issue RISC processor. PEs communicate through a shared multi-ported, multi-bank, tightly-coupled data memory (TCDM, a scratchpad memory). Additionally, STHORM clusters feature an additional core called the <emphasis>cluster controller</emphasis> (CC) and meant, as the name suggests, for the execution of control code local to the cluster operation. Globally, four <emphasis>clusters</emphasis> plus a <emphasis>fabric controller</emphasis> (FC) core &#x02013; responsible for global coordination of the clusters &#x02013; are interconnected via two asynchronous networks-on-chip (ANoC). The first ANoC is used for accessing a multibanked, multiported L2 memory, shared among the four clusters. The second ANoC is used for inter-cluster communication via L1 TCDMs (i.e., remote clusters&#x02019; TCDMs can be accessed by every core in the system) and to access the offchip main memory (L3 DRAM).</para>
<para>STHORM delivers up to 80 GOps (single-precision floating point) with only 2W power consumption.</para>
</section>
<section class="lev2" id="sec2-2-6">
<title>2.2.6 Epiphany-V</title>
<fig id="F2-6" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-6">Figure <xref linkend="F2-6" remap="2.6"/></link></label>
<caption><para>Block diagram of the Epiphany-V chip from Adapteva.</para></caption>
<graphic xlink:href="graphics/ch02_epiphany.jpg"/>
</fig>
<para>The Epiphany-V chip from Adapteva [11] is based on a 1024-core processor in 16 nm FinFet technology. The chip contains an array of 1024 64-bit RISC processors, 64 MB of on-chip SRAM, three 136-bit wide mesh Networks-On-Chip, and 1,024 programmable IO pins.</para>
<para>Similar to the Tilera <emphasis>Tile</emphasis> architecture, the Epiphany architecture is a distributed shared memory architecture composed of an array of RISC processors communicating via a low-latency, mesh-based NoC, as shown in <link linkend="F2-6">Figure <xref linkend="F2-6" remap="2.6"/></link>. Each cluster (or <emphasis>node</emphasis>) in the 2D array features a single, complete RISC processor capable of independently running an operating system [according to the multiple-instruction, multiple-data (MIMD) paradigm]. The distributed shared memory model of the Epiphany-V chip relies on a cache-less design, in which all scratchpad memory blocks are readable and writable by all processors in the system (similar to the STHORM chip).</para>
<para>The Epiphany-V chip can deliver two teraflops of performance (single-precision floating point) in a 2W power envelope.</para>
</section>
<section class="lev2" id="sec2-2-7">
<title>2.2.7 TI Keystone II</title>
<fig id="F2-7" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-7">Figure <xref linkend="F2-7" remap="2.7"/></link></label>
<caption><para>Texas Instrument Keystone II heterogeneous system.</para></caption>
<graphic xlink:href="graphics/ch02_keystone.jpg"/>
</fig>
<para>The Texas Instrument Keystone II [12], is a heterogeneous SoC featuring a quad-core ARM Cortex-A15 and an accelerator cluster comprising eight C66x VLIW DSPs. The chip is designed for special-purpose industrial tasks, such as networking, automotive, and low-power server applications. The 66AK2H12 SoC, depicted in <link linkend="F2-7">Figure <xref linkend="F2-7" remap="2.7"/></link>, is the top-performance Texas Instrument Keystone II device architecture.</para>
<para>Each DSP in the accelerator cluster is a VLIW core, capable of fetching up to eight instructions per cycle and running at up to 1.2 GHz. Locally, a DSP is equipped with 32 KB L1 D-cache and L1 I-cache, plus 1024 KB L2 unified cache. Altogether, the DSPs in the accelerator cluster deliver 160 single-precision GOps.</para>
<para>On the ARM side, there are 32 KB of L1 D-cache and 32 KB of L1 I-cache per core, plus a coherent 4 MB L2 cache.</para>
<para>The computational power of such architecture, at a power budget of up to 14 W, makes it a low-power solution for microserver-class applications. The Keystone II processor has been used in several cloud-computing/microserver settings [13, 14, 15].</para>
</section>
<section class="lev2" id="sec2-2-8">
<title>2.2.8 Kalray MPPA-256</title>
<para>The Kalray MPPA-256 processor of the MPPA (Multi-Purpose Processor Array) MANYCORE family has been developed by the company KALRAY. It is a single-chip programmable manycore processor manufactured in 28 nm CMOS technology that targets low-to-medium volume professional applications, where low energy per operation and time predictability are the primary requirements [16].</para>
<fig id="F2-8" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-8">Figure <xref linkend="F2-8" remap="2.8"/></link></label>
<caption><para>High-level view of the Kalray MPPA-256 processor.</para></caption>
<graphic xlink:href="graphics/ch02_kalray.jpg"/>
</fig>
<para>It concentrates a great potential and is very promising for high-performance parallel computing. With an operating frequency of 400 MHz and a typical power consumption of 5 W, the processor can perform up to 700 GOPS and 230 GFLOPS. The processor integrates a total of 288 identical Very Long Instruction Word (VLIW) cores including 256 user cores referred to as processing engines (PEs) and dedicated to the execution of the user applications and 32 system cores referred to as Resource Manager (RM) and dedicated to the management of the software and processing resources. The cores are organized in 16 compute clusters and four I/O subsystems to control all the I/O devices. In <link linkend="F2-8">Figure <xref linkend="F2-8" remap="2.8"/></link>, the 16 inner nodes (labeled CC) correspond to the 16 compute clusters holding 17 cores each: 16 PEs and 1 RM. Then, there are four I/O subsystems located at the periphery of the chip, each holding four RMs. Each compute cluster and I/O subsystem owns a private address space, while communication and synchronization between them is ensured by the data and control NoC depicted in <link linkend="F2-8">Figure <xref linkend="F2-8" remap="2.8"/></link>. The MPPA-256 processor is also fitted with a variety of I/O controllers, in particular DDR, PCI, Ethernet, Interlaken, and GPIO.</para>
<section class="lev2" id="sec2-2-8-1">
<title>2.2.8.1 The I/O subsystem</title>
<para>The four I/O subsystems (also denoted as IOS) are referenced as the North, South, East, and West IOS. They are responsible for all communications with elements outside the MPPA-256 processor, including the host workstation if the MPPA is used as an accelerator.</para>
<para>Each IOS contains four RMs in a symmetric multiprocessing configuration. These four RMs are connected to a shared, 16-bank parallel memory of 512 KB, they have their own private instruction cache of 32 KB (8-way, set-associative) and share a data cache of 128 KB (also 8-way, set-associative), which ensures data coherency between the cores.</para>
<para>The four IOS are dedicated to PCIe, Ethernet, Interlaken, and other I/O devices. Each one runs either a rich OS such as Linux or an RTOS that supports the MPPA I/O device drivers. They integrate controllers for an 8-lane Gen3 PCIe for a total peak throughput of 16 GB/s full duplex, Ethernet links ranging from 10 MB/s to 40 GB/s for a total aggregate throughput of 80 GB/s, the Interlaken link providing a way to extend the NoC across MPPA-256 chips and other I/O devices in various configurations like UARTs, I2C, SPI, pulse width modulator (PWM), or general purpose IOs (GPIOs). More precisely, the East and West IOS are connected to a quad 10 GB/s Ethernet controller, while the North and South IOS are connected to an 8-lane PCIe controller and to a DDR interface for access to up to 64 GB of external DDR3-1600.</para>
</section>
<section class="lev2" id="sec2-2-8-2">
<title>2.2.8.2 The Network-on-Chip (NoC)</title>
<para>The NoC holds a key role in the average performance of manycore architectures, especially when different clusters need to exchange messages. In the Kalray MPPA-256 processor, the 16 compute clusters and the four I/O subsystems are connected by two explicitly addressed NoC with bi-directional links providing a full duplex bandwidth up to 3.2 GB/s between two adjacent nodes:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>The data NoC (D-NoC). This NoC is optimized for bulk data transfers;</para></listitem>
<listitem>
<para>The control NoC (C-NoC). This NoC is optimized for small messages at low latency.</para></listitem>
</itemizedlist>
<fig id="F2-9" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-9">Figure <xref linkend="F2-9" remap="2.9"/></link></label>
<caption><para>MPPA-256 NoC architecture.</para></caption>
<graphic xlink:href="graphics/ch02_noc.jpg"/>
</fig>
<para>The two NoCs are identical with respect to the nodes, the 2D-wrapped-around torus topology, shown in <link linkend="F2-9">Figure <xref linkend="F2-9" remap="2.9"/></link>, and the wormhole route encoding. They differ at their device interfaces, by the amount of packet buffering in routers, and by the flow regulation at the source available on the D-NoC. NoC traffic through a router does not interfere with the memory buses of the underlying I/O subsystem or compute cluster, unless that router is the destination node. Besides, the D-NoC implements a quality-of-service (QoS) mechanism, thus guaranteeing predictable latencies for all data transfers.</para>
</section>
<section class="lev2" id="sec2-2-8-3">
<title>2.2.8.3 The Host-to-IOS communication protocol</title>
<para>The special hierarchy among the cores in the MPPA-256 processor helps to better divide the workload to be executed on the PEs. When the MPPA-256 is used as an accelerator, tasks are sent to the MPPA-256 processor from a Host workstation. The communication with the MPPA-256 can thus be performed in a couple of steps which can be referred to as Host-to-IOS, IOS-to-Clusters and finally Cluster-to-Cluster communication protocols. The MPPA-256 processor communicates with the Host workstation through I/O subsystems. The chip is connected to the host CPU by a PCle interface and two connectors &#x02013; <emphasis role="strong">Buffer</emphasis> and <emphasis role="strong">MQueue</emphasis> &#x02013; are available for making this link. The RM core that accommodates the task upon the I/O subsystem is referred to as <emphasis role="strong">Master</emphasis> (see <link linkend="F2-10">Figure <xref linkend="F2-10" remap="2.10"/></link>). The processor then executes the received task (referred to as <emphasis role="strong">Master task</emphasis>) as detailed in Section 4.3.1 and at the end of the execution process, it writes the output data in a 4 GB DDR3 RAM memory, which is connected to an I/O subsystem and can be accessed by the host CPU.</para>
<fig id="F2-10" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-10">Figure <xref linkend="F2-10" remap="2.10"/></link></label>
<caption><para>A master task runs on an RM of an I/O subsystem.</para></caption>
<graphic xlink:href="graphics/ch02_hosttoIOS.jpg"/>
</fig>
</section>
<section class="lev2" id="sec2-2-8-4">
<title>2.2.8.4 Internal architecture of the compute clusters</title>
<para>The compute cluster (<link linkend="F2-11">Figure <xref linkend="F2-11" remap="2.11"/></link>) is the basic processing unit of the MPPA architecture. Each cluster contains 17 Kalray-1 VLIW cores, including 16 PE cores dedicated to the execution of the user applications and one RM core. Among other responsibilities, the RM is in charge of mapping and scheduling the threads on the PEs and managing the communications between the clusters and between the clusters and the main memory. The 16 PEs and the RM are connected to a shared memory of 2 MB. A direct memory access (DMA) engine is responsible for transferring data between the shared memory and the NoC or within the shared memory. The DMA engine supports multi-dimensional data transfers and sustains a total throughput of 3.2 GB/s in full duplex. The Debug and System Unit (DSU) supports the compute cluster debug and diagnostics capabilities. Each DSU is connected to the outside world by a JTAG (IEEE 1149.1) chain. The DSU also contains a system trace IP that is used by lightly instrumented code to push up to 1.6 GB/s of trace data to an external acquisition device. This trace data gives almost non-intrusive insight on the behaviour of the application.</para>
<fig id="F2-11" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-11">Figure <xref linkend="F2-11" remap="2.11"/></link></label>
<caption><para>Internal architecture of a compute cluster.</para></caption>
<graphic xlink:href="graphics/ch02_clusterintern.jpg"/>
</fig>
</section>
<section class="lev2" id="sec2-2-8-5">
<title>2.2.8.5 The shared memory</title>
<para>The shared memory (SMEM) in each compute cluster (yellow box in <link linkend="F2-11">Figure <xref linkend="F2-11" remap="2.11"/></link>) comprises 16-banked independent memory of 16,384 x 64-bit words = 128 kB per bank, with a total capacity of 16 x 128 kB = 2 MB, with error code correction (ECC) on 64-bit words. This memory space is shared between the 17 VLIW cores in the cluster and delivers an aggregate bandwidth of 38.4 GB/s.</para>
<para>The 16 memory banks are arranged in two sides of eight banks, the left side and the right side. The connections between the memory bus masters are replicated in order to provide independent access to the two sides. There are two ways of mapping a physical address to a specific side and bank.</para>
<para><emphasis role="strong">Option 1 (Interleaving address mapping)</emphasis> &#x02013; In the address space, bits 6&#x02013;9 of the byte address select the memory bank, so sequential addresses move from one bank to another every 64 bytes (every 8 x 64-bit words), as depicted in <link linkend="F2-12">Figure <xref linkend="F2-12" remap="2.12"/></link>. This address-mapping scheme is effective at distributing the requests of cores across memory banks, while ensuring that each cache refill request involves only one memory bank and benefits from a burst access mode. Furthermore, this address scheme also allows the &#x0201C;simultaneous&#x0201D; access (respecting the activation time) of those memory banks in which the cache line is stored. As the side selection depends on the sixth bit of the byte address, the bank selection by sequential addresses alternates between the left side and the right side every 64 bytes.</para>
<fig id="F2-12" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-12">Figure <xref linkend="F2-12" remap="2.12"/></link></label>
<caption><para>Memory accesses distributed across memory banks (interleaved).</para></caption>
<graphic xlink:href="graphics/ch02_interleaved.jpg"/>
</fig>
<para><emphasis role="strong">Option 2 (Contiguous address mapping)</emphasis> &#x02013; It is possible to disable the memory address shuffing, in which case each bank has a sequential address space covering one bank of 128 KB as depicted in <link linkend="F2-13">Figure <xref linkend="F2-13" remap="2.13"/></link>. The high-order bit of the address selects the side (i.e., the right side covers addresses from 0 to 1 MB and the left side covers addresses above 1 MB). When zero interference between cores is needed, cores within a given pair must use a different side.</para>
<fig id="F2-13" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F2-13">Figure <xref linkend="F2-13" remap="2.13"/></link></label>
<caption><para>Memory accesses targeting a same memory bank (contiguous).</para></caption>
<graphic xlink:href="graphics/ch02_contiguous.jpg"/>
</fig>
</section>
</section></section>
<section class="lev1" id="sec2-3">
<title>2.3 Summary</title>
<para>Back in the early days of the new millennium, multicore processors allowed computer designers to overcome several technological <emphasis>walls</emphasis> that traditional single-core design methodologies were no longer capable of addressing. This design paradigm is to date the standard, with an ever-increasing number of processing cores integrated on the same chip. While manycore processors enabled over the past 15 years the seamless continuation of compute performance scalability for general-purpose and scientific workloads, real-time systems have not been able to embrace this technology so far, due to the lack of predictability in execution time implied by hardware resource sharing. This chapter has surveyed several state-of-the-art manycore processors, highlighting the architectural features (i) that enable processor integration scalability and (ii) those that are shared among several processor and that are mostly responsible for the unpredictable execution.</para>
</section>
<section class="lev1" id="sec2-4">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Olukotun, K., Nayfeh, B. A., Hammond, L., Wilson, K., and Chang, K., The case for a single-chip multiprocessor. <emphasis>SIGOPS Oper. Syst. Rev.</emphasis>, 30, 2&#x02013;11, 1996. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Olukotun%2C+K%2E%2C+Nayfeh%2C+B%2E+A%2E%2C+Hammond%2C+L%2E%2C+Wilson%2C+K%2E%2C+and+Chang%2C+K%2E%2C+The+case+for+a+single-chip+multiprocessor%2E+SIGOPS+Oper%2E+Syst%2E+Rev%2E%2C+30%2C+2-11%2C+1996%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Fuller, S. H., and Millett, L. I., Computing performance: Game over or next level? <emphasis>Computer</emphasis>, 44, 31&#x02013;38, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Fuller%2C+S%2E+H%2E%2C+and+Millett%2C+L%2E+I%2E%2C+Computing+performance%3A+Game+over+or+next+level%B4+Computer%2C+44%2C+31-38%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Hennessy, J. L., and Patterson, D. A., <emphasis>Computer Architecture: A Quantitative Approach</emphasis>. Elsevier, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Hennessy%2C+J%2E+L%2E%2C+and+Patterson%2C+D%2E+A%2E%2C+Computer+Architecture%3A+A+Quantitative+Approach%2E+Elsevier%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Patterson, D. A., Latency lags bandwith. <emphasis>Commun. ACM</emphasis>, 47, 71&#x02013;75, 2004. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Patterson%2C+D%2E+A%2E%2C+Latency+lags+bandwith%2E+Commun%2E+ACM%2C+47%2C+71-75%2C+2004%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Agarwal, V., Hrishikesh, M. S., Keckler, S. W., and Burger, D., &#x0201C;Clock rate versus ipc: the end of the road for conventional microarchitectures.&#x0201D; In <emphasis>Proceedings of 27th International Symposium on Computer Architecture (IEEE Cat. No.RS00201)</emphasis>, pages 248&#x02013;259, 2000. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Agarwal%2C+V%2E%2C+Hrishikesh%2C+M%2E+S%2E%2C+Keckler%2C+S%2E+W%2E%2C+and+Burger%2C+D%2E%2C+%22Clock+rate+versus+ipc%3A+the+end+of+the+road+for+conventional+microarchitectures%2E%22+In+Proceedings+of+27th+International+Symposium+on+Computer+Architecture+%28IEEE+Cat%2E+No%2ERS00201%29%2C+pages+248-259%2C+2000%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Naffziger, S., and Sohi, G., Hot chips 26. <emphasis>IEEE Micro</emphasis>. 35, 4&#x02013;5, 2015.</para></listitem>
<listitem>
<para>Tabuchi, A., Kimura, Y., Torii, S., Matsufuru, H., Ishikawa, T., Boku, T., and Sato, M., <emphasis>Design and Preliminary Evaluation of Omni OpenACC Compiler for Massive MIMD Processor PEZY-SC</emphasis>, pp. 293&#x02013;305, Springer International Publishing, Cham, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Tabuchi%2C+A%2E%2C+Kimura%2C+Y%2E%2C+Torii%2C+S%2E%2C+Matsufuru%2C+H%2E%2C+Ishikawa%2C+T%2E%2C+Boku%2C+T%2E%2C+and+Sato%2C+M%2E%2C+Design+and+Preliminary+Evaluation+of+Omni+OpenACC+Compiler+for+Massive+MIMD+Processor+PEZY-SC%2C+pp%2E+293-305%2C+Springer+International+Publishing%2C+Cham%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>NVIDIA SRL. <emphasis>Whitepaper: NVIDIA Tegra X1 &#x02013; NVIDIA&#x02019;s New Mobile Superchip</emphasis>. <ulink url="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html">https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html</ulink>, accessed November 07, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=NVIDIA+SRL%2E+Whitepaper%3A+NVIDIA+Tegra+X1+-+NVIDIA%27s+New+Mobile+Superchip%2E+https%3A%2F%2Fdocs%2Envidia%2Ecom%2Fcuda%2Fcuda-c-programming-guide%2Findex%2Ehtml%2C+accessed+November+07%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Taylor, M. B., Kim, J., Miller, J., Wentzlaff, D., Ghodrat, F., Greenwald, B., et al. The raw microprocessor: a computational fabric for software circuits and general-purpose programs. <emphasis>IEEE Micro</emphasis>, 22, 25&#x02013;35, 2002. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Taylor%2C+M%2E+B%2E%2C+Kim%2C+J%2E%2C+Miller%2C+J%2E%2C+Wentzlaff%2C+D%2E%2C+Ghodrat%2C+F%2E%2C+Greenwald%2C+B%2E%2C+The+raw+microprocessor%3A+a+computational+fabric+for+software+circuits+and+general-purpose+programs%2E+IEEE+Micro%2C+22%2C+25-35%2C+2002%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Melpignano, D., Benini, L., Flamand, E., Jego, B., Lepley, T., Haugou, G., Clermidy, F., and Dutoit, D., &#x0201C;Platform 2012, a many-core computing accelerator for embedded socs: Performance evaluation of visual analytics applications.&#x0201D; In <emphasis>Proceedings of the 49th Annual Design Automation Conference</emphasis>, DAC &#x02019;12, pp. 1137&#x02013;1142, New York, NY, USA, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Melpignano%2C+D%2E%2C+Benini%2C+L%2E%2C+Flamand%2C+E%2E%2C+Jego%2C+B%2E%2C+Lepley%2C+T%2E%2C+Haugou%2C+G%2E%2C+Clermidy%2C+F%2E%2C+and+Dutoit%2C+D%2E%2C+%22Platform+2012%2C+a+many-core+computing+accelerator+for+embedded+socs%3A+Performance+evaluation+of+visual+analytics+applications%2E%22+In+Proceedings+of+the+49th+Annual+Design+Automation+Conference%2C+DAC+%2712%2C+pp%2E+1137-1142%2C+New+York%2C+NY%2C+USA%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Olofsson, A., Epiphany-v: A 1024 processor 64-bit RISC system-on-chip. <emphasis>CoRR</emphasis>, abs/1610.01832, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Olofsson%2C+A%2E%2C+Epiphany-v%3A+A+1024+processor+64-bit+RISC+system-on-chip%2E+CoRR%2C+abs%2F1610%2E01832%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Stotzer, E., Jayaraj, A., Ali, M., Friedmann, A., Mitra, G., Rendell, A. P., and Lintault, I., <emphasis>OpenMP on the Low-Power TI Keystone II ARM/DSP System-on-Chip</emphasis>, pp. 114&#x02013;127, Springer Berlin Heidelberg, Berlin, Heidelberg, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Stotzer%2C+E%2E%2C+Jayaraj%2C+A%2E%2C+Ali%2C+M%2E%2C+Friedmann%2C+A%2E%2C+Mitra%2C+G%2E%2C+Rendell%2C+A%2E+P%2E%2C+and+Lintault%2C+I%2E%2C+OpenMP+on+the+Low-Power+TI+Keystone+II+ARM%2FDSP+System-on-Chip%2C+pp%2E+114-127%2C+Springer+Berlin+Heidelberg%2C+Berlin%2C+Heidelberg%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Verma, A., and Flanagan, T., A Better Way to Cloud. <emphasis>Texas Instruments white paper</emphasis>, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Verma%2C+A%2E%2C+and+Flanagan%2C+T%2E%2C+A+Better+Way+to+Cloud%2E+Texas+Instruments+white+paper%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Hewlett-Packard Development Company L.P. <emphasis>HP ProLiant m800 Server Cartridge</emphasis>.</para></listitem>
<listitem>
<para>nCore HPC LLC. <emphasis>Brown Dwarf Y-Class Supercomputer</emphasis>.</para></listitem>
<listitem>
<para>Amdahl, G. M., &#x0201C;Validity of the single processor approach to achieving large scale computing capabilities.&#x0201D; In <emphasis>Proceedings of the April 18&#x02013;20, 1967, Spring Joint Computer Conference, AFIPS &#x02019;67</emphasis> (Spring), pp. 483&#x02013;485, New York, NY, USA, 1967.</para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch03" label="3" xreflabel="3">
<title>Predictable Parallel Programming with OpenMP</title>
<para><emphasis role="strong">Maria A. Serrano<superscript>1</superscript>, Sara Royuela<superscript>1</superscript>, Andrea Marongiu<superscript>2</superscript> and Eduardo Qui&#x000F1;ones<superscript>1</superscript></emphasis></para>
<para><superscript>1</superscript>Barcelona Supercomputing Center (BSC), Spain<?lb?><superscript>2</superscript>Swiss Federal Institute of Technology in Z&#x000FC;rich (ETHZ), Switzerland; and University of Bologna, Italy</para>
<para>This chapter motivates the use of the OpenMP (Open Multi-Processing) parallel programming model to develop future critical real-time embedded systems, and analyzes the time-predictable properties of the OpenMP tasking model. Moreover, this chapter presents the set of compiler techniques needed to extract the timing information of an OpenMP program in the form of an <emphasis>OpenMP Direct Acyclic Graph</emphasis> or OpenMP-DAG.</para>
<section class="lev1" id="sec3-1">
<title>3.1 Introduction</title>
<para>Parallel programming models are key to increase the productivity of parallel software from three different angles:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>From a <emphasis>programmability</emphasis> angle, parallel programming models provide developers with the abstraction level required to program parallel applications while hiding processor complexities.</para></listitem>
<listitem>
<para>From a <emphasis>portability</emphasis> angle, platform-independent parallel programming models allow executing the same parallel source code in different parallel platforms.</para></listitem>
<listitem>
<para>From a <emphasis>performance</emphasis> angle, different levels of abstraction allow for a fine-tuned parallelism, i.e., users may either squeeze the capabilities of a specific architecture using the language capabilities, or rely on runtime mechanisms to dynamically exploit parallelism.</para></listitem>
</orderedlist>
<para>Hence, parallel programming models are of paramount importance to exploit the massive computation capabilities of state-of-the-art and future parallel and heterogeneous processor architectures. Several approaches coexist with such a goal, and these can be grouped as follows [1]:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis>Hardware-centric models</emphasis> aim to replace the native platform programming with higher-level, user-friendly solutions, e.g., Intel<superscript>&#x00AE;</superscript> TBB [2] and NVIDIA<superscript>&#x00AE;</superscript> CUDA [3]. These models focus on tuning an application to match a chosen platform, which makes their use neither a scalable nor a portable solution.</para></listitem>
<listitem>
<para><emphasis>Application-centric models</emphasis> deal with the application parallelization from design to implementation, e.g., OpenCL [4]. Although portable, these models may require a full rewriting process to accomplish productivity.</para></listitem>
<listitem>
<para><emphasis>Parallelism-centric models</emphasis> allow users to express typical parallelism constructs in a simple and effective way, and at various levels of abstraction, e.g., POSIX threads [6] and OpenMP [7]. This approach allows flexibility and expressiveness, while decoupling design from implementation.</para></listitem>
</itemizedlist>
<para>Considering the vast amount of parallel programming models available, there is a noticeable need to unify programming models to exploit the performance benefits of parallel and heterogeneous architectures [9]. In that sense, OpenMP has proved many advantages over its competitors to enhance productivity. The next sections introduce the main characteristics of the most relevant programming models, and conclude with an analysis of the main benefits of OpenMP.</para>
<section class="lev2" id="sec3-1-1">
<title>3.1.1 Introduction to Parallel Programming Models</title>
<para>The multitude of parallel programming models currently existing makes it difficult to choose the language that better fits the needs of each particular case. <link linkend="T3-1">Table <xref linkend="T3-1" remap="3.1"/></link> introduces the main characteristics of the most relevant programming models in critical embedded systems. The features considered are the following: performance (based on throughput, bandwidth, and other metrics), portability (based on how straight-forward it is to migrate to different environments), heterogeneity (based on the support for cross-platform applications), parallelism (based on the support provided for data-based and task-based parallelism), programmability (based on how easy it is for programmers to get the best results), and flexibility (based on the features for parallelizing offered in the language).</para>
<table-wrap position="float" id="T3-1">
<label><link linkend="T3-1">Table <xref linkend="T3-1" remap="3.1"/></link></label>
<caption><para>Parallel programming models comparison</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="bottom" align="left"></td>
<td valign="bottom" align="center">Pthreads</td>
<td valign="bottom" align="center">OpenCL</td>
<td valign="bottom" align="center">CUDA</td>
<td valign="bottom" align="center">Cilk Plus</td>
<td valign="bottom" align="center">TBB</td>
<td valign="bottom" align="center">OpenMP</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left">Performance</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td></tr>
<tr>
<td valign="top" align="left">Portability</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td>
</tr>
<tr>
<td valign="top" align="left">Heterogeneity</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td>
</tr>
<tr>
<td valign="top" align="left">Parallelism</td>
<td valign="top" align="center">data/task</td>
<td valign="top" align="center">data/task</td>
<td valign="top" align="center">data</td>
<td valign="top" align="center">data/task</td>
<td valign="top" align="center">task</td>
<td valign="top" align="center">data/task</td>
</tr>
<tr>
<td valign="top" align="left">Programmability</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
</tr>
<tr>
<td valign="top" align="left">Flexibility</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x000D7;</td>
<td valign="top" align="center">&#x02713;</td>
<td valign="top" align="center">&#x02713;&#x02713;</td></tr>
</tbody>
</table>
</table-wrap>
<section class="lev2" id="sec3-1-1-1">
<title>3.1.1.1 POSIX threads</title>
<para>POSIX threads (Portable Operating System Interface for UNIX threads), usually referred to as Pthreads, is a standard C language programming interface for UNIX systems. The language provides efficient light-weight mechanisms for thread management and synchronization, including mutual exclusion and barriers.</para>
<para>In a context where hardware vendors used to implement their own proprietary versions of threads, Pthreads arose with the aim of enhancing the portability of threaded applications that reside on shared memory platforms. However, Pthreads results in very poor programmability, due to the low-level threading model provided by the standard, that leaves most of the implementation details to the programmer (e.g., work-load partitioning, worker management, communication, synchronization, and task mapping). Overall, the task of developing applications with Pthreads is very hard.</para>
</section>
<section class="lev2" id="sec3-1-1-2">
<title>3.1.1.2 OpenCL<superscript>TM</superscript></title>
<para>OpenCL<superscript>TM</superscript> (Open Computing Language) is an open low-level application programming interface (API) for cross-platform parallel computing that runs on heterogeneous systems including multicore and manycore CPUs, GPUs, DSPs, and FPGAs. There are two different actors in an OpenCL system: the host and the devices. The language specifies a programming language based on C99 used to control the host, and a standard interface for parallel computing, which exploits task-based and data-based parallelism, used to control the devices.</para>
<para>OpenCL can run in a large variety of devices, which makes portability its most valuable characteristic. However, the use of vendor-specific features may prevent this portability, and codes are not guaranteed to be optimal due to the important differences between devices. Furthermore, the language has an important drawback: it is significantly difficult to learn, affecting the programmability.</para>
</section>
<section class="lev2" id="sec3-1-1-3">
<title>3.1.1.3 NVIDIA<superscript>&#x00AE;</superscript> CUDA</title>
<para>NVIDIA<superscript>&#x00AE;</superscript> CUDA is a parallel computing platform and API for exploiting CUDA-enabled GPUs for general-purpose processing. The platform provides a layer that gives direct access to the GPU&#x02019;s instruction set, and is accessible through CUDA-accelerated libraries, compiler directives (such as OpenACC [10]), and extensions to industry-standard programming languages (such as C and C++).</para>
<para>The language provides dramatic increases of performance when exploiting parallelism in GPGPUs. However, its use is limited to CUDA-enabled GPUs, which are produced only by NVIDIA<superscript>&#x00AE;</superscript>. Furthermore, tuning applications with CUDA may be hard because it requires rewriting all the offoaded kernels and knowing the specifics of each platform to get the best results.</para>
</section>
<section class="lev2" id="sec3-1-1-4">
<title>3.1.1.4 Intel<superscript>&#x00AE;</superscript> Cilk<superscript>TM</superscript> Plus</title>
<para>Intel<superscript>&#x00AE;</superscript> Cilk Plus [11] is an extension to C/C++ based on Cilk++ [12] that has become popular because of its simplicity and high level of abstraction. The language provides support for both data and task parallelism, and provides a framework that optimizes load balance, implementing a work-stealing mechanism to execute tasks [13].</para>
<para>The language provides a simple yet efficient platform for implementing parallelism. Nonetheless, portability is very limited because only Intel<superscript>&#x00AE;</superscript> and GCC implement support for the language extensions defined by Cilk Plus. Furthermore, the possibilities available with this language are limited to tasks (<emphasis>_cilk_spawn</emphasis>, <emphasis>_cilk_sync</emphasis>), loops (<emphasis>_cilk_for</emphasis>), and reductions (<emphasis>reducers</emphasis>).</para>
</section>
<section class="lev2" id="sec3-1-1-5">
<title>3.1.1.5 Intel<superscript>&#x00AE;</superscript> TBB</title>
<para>Intel<superscript>&#x00AE;</superscript> TBB is an object-oriented C++ template library for implementing task-based parallelism. The language offers constructs for parallel loops, reductions, scans, and pipeline parallelism. The framework provided has two key components: (1) compilers, which optimize the language templates enabling a low-overhead form of polymorphism, and (2) runtimes, which keep temporal locality by implementing a queue of tasks for each worker, and balance workload across available cores by implementing a work-stealing policy.</para>
<para>TBB offers a high level of abstraction in front of complicated low-level APIs. However, adapting the code to fit the library templates can be arduous. Furthermore, portability is limited, although the last releases support Visual C++, Intel<superscript>&#x00AE;</superscript> C++ compiler, and the GNU compiler collection.</para>
</section>
<section class="lev2" id="sec3-1-1-6">
<title>3.1.1.6 OpenMP</title>
<para>OpenMP, the de-facto standard parallel programming model for shared memory architectures in the high-performance computing (HPC) domain, is increasingly adopted also in embedded systems. The language was originally focused on a thread-centric model to exploit massive data-parallelism and loop intensive applications. However, the latest specifications of OpenMP have evolved to a task-centric model that enables very sophisticated types of fine-grained and irregular parallelism, and also include a host-centric accelerator model that enables an efficient exploitation of heterogeneous systems. As a matter of fact, OpenMP is supported in the SDK of many of the state-of-the-art parallel and heterogeneous embedded processor architectures, e.g., Kalray MPPA [14], and TI Keystone II [16].</para>
<para>Different evaluations demonstrate that OpenMP delivers tantamount performance and efficiency compared to highly tunable models such as TBB [17], CUDA [18] and OpenCL [19]. Moreover, OpenMP has different advantages over low-level libraries such as Pthreads: on one hand, it offers robustness without sacrificing performance [21] and, on the other hand, OpenMP does not lock the software to a specific number of threads. Another important advantage is that the code can be compiled as a single-threaded application just disabling support for OpenMP, thus easing debugging and so programmability.</para>
<para>Overall, the use of OpenMP presents three main advantages. First, an expert community has constantly reviewed and augmented the language for the past 20 years. Second, OpenMP is widely implemented by several chip and compiler vendors from both high-performance and embedded computing domains (e.g., GNU, Intel<superscript>&#x00AE;</superscript>, ARM, Texas Instruments and IBM), increasing portability among multiple platforms from different vendors. Third, OpenMP provides greater expressiveness due to years of experience in its development; the language offers several directives for parallelization and fine-grained synchronization, along with a large number of clauses that allow it to contextualize concurrency and heterogeneity, providing fine control of the parallelism.</para>
</section>
</section></section>
<section class="lev1" id="sec3-2">
<title>3.2 The OpenMP Parallel Programming Model</title>
<section class="lev2" id="sec3-2-1">
<title>3.2.1 Introduction and Evolution of OpenMP</title>
<para>OpenMP represents the computing resources of a parallel processor architecture (i.e., cores) by means of high-level threads, named <emphasis>OpenMP threads</emphasis>, upon which programmers can assign units of code to be executed. During the execution of the program, the OpenMP runtime assigns these threads to low-level computing resources, i.e., the operating system (OS) threads, which are then assigned to physical cores by the OS scheduler, following the execution model defined by the OpenMP directives. <link linkend="F3-1">Figure <xref linkend="F3-1" remap="3.1"/></link> shows a schematic view of the stack of components involved in the execution of an OpenMP program. OpenMP exposes some aspects of managing OpenMP threads to the user (e.g., defining the number of OpenMP threads assigned to a parallel execution by means of the <literal>num_threads</literal> clause). The rest of components are transparent to the user and efficiently managed by the OpenMP runtime and the OS.</para>
<fig id="F3-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-1">Figure <xref linkend="F3-1" remap="3.1"/></link></label>
<caption><para>OpenMP components stack.</para></caption>
<graphic xlink:href="graphics/ch03_omp_stack.jpg"/>
</fig>
<para>Originally, up to OpenMP version 2.5 [22], OpenMP was traditionally focused on massively data-parallel, loop-intensive applications, following the <emphasis>single-program-multiple-data</emphasis> programming paradigm. In this model, known as <emphasis>thread model</emphasis>, OpenMP threads are visible to the programmer, which are controlled with work-sharing constructs that assign iterations of a loop or code segments to OpenMP threads.</para>
<para>The OpenMP 3.0 specification [23] introduced the concept of tasks by means of the <literal>task</literal> directive, which exposes a higher level of abstraction to programmers. A task is an independent parallel unit of work, which defines an instance of code and its data environment. This new model, known as <emphasis>tasking model</emphasis>, provides a very convenient abstraction of parallelism as it is the runtime (and not the programmer) the responsible for scheduling tasks to threads.</para>
<para>With version 4.0 of the specification [24], OpenMP evolved to consider very sophisticated types of fine-grained, irregular and highly unstructured parallelism, with mature support to express dependences among tasks. Moreover, it incorporated for the first time a new accelerator model including features for offoading computation and performing data transfers between the host and one or more accelerator devices. The latest version, OpenMP 4.5 [25], enhances the previous accelerator model by coupling it with the tasking model.</para>
<para><link linkend="F3-2">Figure <xref linkend="F3-2" remap="3.2"/></link> shows a time-line of all existent releases of OpenMP, since 1997, when the OpenMP Architecture Review Board (ARB) was formed. The next version, 5.0 [26&#x02013;28], is planned for November 2018.</para>
<fig id="F3-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-2">Figure <xref linkend="F3-2" remap="3.2"/></link></label>
<caption><para>OpenMP releases time-line.</para></caption>
<graphic xlink:href="graphics/ch03_omp_timeline.jpg"/>
</fig>
</section>
<section class="lev2" id="sec3-2-2">
<title>3.2.2 Parallel Model of OpenMP</title>
<para>This section provides a brief description of the OpenMP parallel programming model as defined in the latest specification, version 4.5.</para>
<section class="lev2" id="sec3-2-2-1">
<title>3.2.2.1 Execution model</title>
<para>An OpenMP program begins as a single thread of execution, called the <emphasis>initial thread</emphasis>. Parallelism is achieved through the <literal>parallel</literal> construct, in which a new <emphasis>team</emphasis> of OpenMP threads is spawned. OpenMP allows programmers to define the amount of threads desired for a parallel region by means of the <literal>num_threads</literal> clause attached to the <literal>parallel</literal> construct. The spawned threads are joined at the implicit barrier encountered at the end of the parallel region. This is the so-called <emphasis>fork-join model</emphasis>. Within the parallel region, parallelism can be distributed in two ways that provide tantamount performance [29]:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>The <emphasis>thread-centric model</emphasis> exploits structured parallelism distributing work by means of work-sharing constructs (e.g., <literal>for</literal> and <literal>sections</literal> constructs). It provides a fine-grained control of the mapping between work and threads, as well as a coarse grain synchronization mechanism by means of the <literal>barrier</literal> construct.</para></listitem>
<listitem>
<para>The <emphasis>task-centric model</emphasis>, or simply <emphasis>tasking model</emphasis>, exploits both structured and unstructured parallelism distributing work by means of tasking constructs (e.g., <literal>task</literal> and <literal>taskloop</literal> constructs). It provides a higher level of abstraction in which threads are mainly controlled by the runtime, as well as fine-grained synchronization mechanisms by means of the <literal>taskwait</literal> construct and the <literal>depend</literal> clause that, attached to a <literal>task</literal> construct, allow the description of a list of <emphasis>input</emphasis> and/or <emphasis>output</emphasis> dependences. A task with an <literal>in</literal>, <literal>out</literal> or <literal>inout</literal> dependence is ready to execute when all previous tasks with an <literal>out</literal> or <literal>inout</literal> dependence on the same storage location complete.</para></listitem>
</orderedlist>
<para><link linkend="F3-3">Figure <xref linkend="F3-3" remap="3.3"/></link> shows the execution model of a parallel loop implemented with the <literal>for</literal> directive, where all spawned threads work in parallel from the beginning of the parallel region as long as there is work to do. <link linkend="F3-4">Figure <xref linkend="F3-4" remap="3.4"/></link> shows the model of a parallel block with unstructured tasks. In this case, the <literal>single</literal> construct restricts the execution of the parallel region to only one thread until a <literal>task</literal> construct is found. Then, another thread (or the same, depending on the scheduling policy), concurrently executes the code of the task. In <link linkend="F3-3">Figure <xref linkend="F3-3" remap="3.3"/></link>, the colours represent the execution of differents iterations of the same parallel loop; in <link linkend="F3-4">Figure <xref linkend="F3-4" remap="3.4"/></link>, colours represent the parallel execution of the code included within a task construct.</para>
<fig id="F3-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-3">Figure <xref linkend="F3-3" remap="3.3"/></link></label>
<caption><para>Structured parallelism.</para></caption>
<graphic xlink:href="graphics/ch03_fork-join-for.jpg"/>
</fig>
<fig id="F3-4" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-4">Figure <xref linkend="F3-4" remap="3.4"/></link></label>
<caption><para>Unstructured parallelism.</para></caption>
<graphic xlink:href="graphics/ch03_fork-join-task.jpg"/>
</fig>
</section>
<section class="lev2" id="sec3-2-2-2">
<title>3.2.2.2 Acceleration model</title>
<para>OpenMP also provides a <emphasis>host-centric accelerator model</emphasis> in which a host offoads data and code to the accelerator devices available in the same processor architecture for execution by means of the <literal>target</literal> construct. When a <literal>target</literal> directive is encountered, a new <emphasis>target task</emphasis> enclosing the target region is generated. The target task is completed after the execution of the target region finishes. One of the most interesting characteristics of the accelerator model is its integration with the tasking model. Note that each accelerator device has its own team of threads that are distinct from threads that execute on another device, and these cannot migrate from one device to another.</para>
<para>In case the accelerator device is not available or even does not exist (this may occur when the code is ported from one architecture to another) the target region is executed in the host. The <literal>map</literal> clause associated with the target construct specifies the data items that will be mapped to/from the target device. Further parallelism can be exploited within the target device.</para>
</section>
<section class="lev2" id="sec3-2-2-3">
<title>3.2.2.3 Memory model</title>
<para>OpenMP is based on a relaxed-consistency, shared-memory model. This means there is a memory space shared for all threads, called <emphasis>memory</emphasis>. Additionally, each thread has a temporary view of the memory. The temporary view is not always required to be consistent with the memory. Instead, each private view synchronizes with the main memory by means of the <emphasis>flush</emphasis> operation, which can be implicit (due to operations causing a memory fence) or explicit (using the <literal>flush</literal> operation). Data cannot be directly synchronized between two different threads temporary view.</para>
<para>The view of each thread has of a given variable is defined using data-sharing clauses, which can determine the following sharing scopes:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><literal>private</literal>: a new fresh variable is created within the scope.</para></listitem>
<listitem>
<para><literal>firstprivate</literal>: a new variable is created in the scope and initialized with the value of the original variable.</para></listitem>
<listitem>
<para><literal>lastprivate</literal>: a new variable is created within the scope and the original variable is updated at the end of the execution of the region (only for tasks).</para></listitem>
<listitem>
<para><literal>shared</literal>: the original variable is used in the scope, thus opening the possibility of data race conditions.</para></listitem>
</itemizedlist>
<para>The use of data-sharing clauses is particularly powerful to avoid unnecessary synchronizations as well as race conditions. All variables appearing within a construct have a default data-sharing defined by the OpenMP specification ([25] Section 2.15.1). These rules are not based on the use of the variables, but on their storage. Thus, users are duty-bound to explicitly scope many variables, changing the default data-sharing values, in order to fulfill correctness (e.g., avoiding data races) and enhance performance (e.g., avoiding unnecessary privatizations).</para>
<table-wrap position="float" id="L3-1">
<label>Listing 3.1</label>
<caption><para>OpenMP example of the tasking and the accelerator models combined</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/list3-1.jpg"/>
</table-wrap>
</section>
</section>
<section class="lev2" id="sec3-2-3">
<title>3.2.3 An OpenMP Example</title>
<para>Listing 3.1 illustrates an OpenMP program that uses both the tasking and the accelerator models. The code enclosed in the <literal>parallel</literal> construct (line 4) defines a team of four OpenMP threads on the host device. The <literal>single</literal> construct (line 6) specifies that only one thread starts executing the associated block of code, while the rest of threads in the team remain waiting. When the task regions are created (lines 9 and 11), each one is assigned to one thread in the team (may be the same thread), and the corresponding output dependences on variables <literal>x</literal> and <literal>y</literal> are stored. When the target task (lines 13:14) is created, its dependences on <literal>x</literal> and <literal>y</literal> are checked. If the tasks producing these variables are finished, then the target task can be scheduled. Otherwise, it must be deferred until the tasks from which it depends have finished. When the target task is scheduled, the code contained in the target region and the variables in the <literal>map(to:)</literal> clause (<literal>x</literal> and <literal>y</literal>) are copied to the accelerator device. After its execution, the <literal>res</literal> variable is copied back to the host memory as defined by the <literal>map(from:)</literal> clause. The presence of a <literal>nowait</literal> clause in the target task allows the execution on the host to continue after the target task is created. All OpenMP threads are guaranteed to be synchronized at the implicit barrier included at the end of the <literal>parallel</literal> and <literal>single</literal> constructs (lines 16 and 19 respectively). A <literal>nowait</literal> clause could be added to the <literal>single</literal> construct to avoid unnecessary synchronizations.</para>
</section>
</section>
<section class="lev1" id="sec3-3">
<title>3.3 Timing Properties of the OpenMP Tasking Model</title>
<para>The tasking model of OpenMP not only provides a very convenient abstraction layer upon which programmers can efficiently develop parallel applications, but also has certain similarities with the <emphasis>sporadic direct acyclic graph (DAG) scheduling model</emphasis> used to derive a (worst-case) response time analysis of parallel applications. <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link> presents in detail the response time analyses that can be applied to the OpenMP tasking model. This section derives the OpenMP-DAG upon which these analyses are applied.</para>
<section class="lev2" id="sec3-3-1">
<title>3.3.1 Sporadic DAG Scheduling Model of Parallel Applications</title>
<para>Real-time embedded systems are often composed of a collection of periodic processing stages applied on different input data streaming coming from sensors. Such a structure makes the system amenable to timing analysis methods [30].</para>
<para>The <emphasis>task model</emphasis> [31], either <emphasis>sporadic or periodic</emphasis>, is a well-known model in scheduling theory to represent real-time systems. In this model, real-time applications are typically represented as a set of <emphasis>n recurrent</emphasis> tasks &#x003C4; = &#x0007B;&#x003C4;<subscript>1</subscript>,&#x003C4;<subscript>2</subscript>,..,&#x003C4;<subscript><emphasis>n</emphasis></subscript>&#x0007D;, each characterized by three parameters: worst-case execution time (<emphasis>WCET</emphasis>), period (<emphasis>T</emphasis>) and relative deadline (<emphasis>D</emphasis>). Tasks repeatedly emit an infinite sequence of <emphasis>jobs</emphasis>. In case of periodic tasks, jobs arrive strictly periodically separated by the fixed interval time <emphasis>T</emphasis>. In case of sporadic tasks, jobs do not have a strict arrival time, but it is assumed that a new job released at time <emphasis>t</emphasis> must finish before <emphasis>t + D</emphasis>. Moreover, a minimum interval of time <emphasis>T</emphasis> must occur between two consecutive jobs from the same task.</para>
<para>With the introduction of multi-core processors, new scheduling models have been proposed to better express the parallelism that these architectures offer. This is the case of the <emphasis>sporadic DAG task model</emphasis> [32&#x02013;36], which allows the exploitation of parallelism <emphasis>within</emphasis> tasks. In the sporadic DAG task model each task (called <emphasis>DAG-task</emphasis>) is represented with a <emphasis>directed acyclic graph</emphasis> (DAG) <emphasis>G = (V,E), T</emphasis> and <emphasis>D</emphasis>. Each node <emphasis>&#x028B; &#x02208; V</emphasis> denotes a sequential operation characterized by a WCET estimation. Edges represent dependences between nodes: if <emphasis>e</emphasis> = (<emphasis>&#x028B;</emphasis><subscript>1</subscript>, <emphasis>&#x028B;</emphasis><subscript>2</subscript>) : <emphasis>e &#x02208; E</emphasis>, then the node <emphasis>&#x028B;</emphasis><subscript>1</subscript> must complete its execution before node <emphasis>&#x028B;</emphasis><subscript>2</subscript> can start executing. In other words, the DAG captures scheduling constraints imposed by dependences among nodes and it is annotated with the WCET estimation of each individual node.</para>
<para>Overall, the DAG represents the main formalism to capture the properties of a real-time application. In that context, although the current specification of OpenMP lacks any notion of real-time scheduling semantics, such as deadline, period, or WCET, the structure and syntax of an OpenMP program have certain similarities with the DAG model. The <literal>task</literal> and <literal>taskwait</literal> constructs, together with the <literal>depend</literal> clause, are very convenient for describing a DAG. Intuitively, a <literal>task</literal> describes a node in <emphasis>V</emphasis> in the DAG model, while <literal>taskwait</literal> constructs and <literal>depend</literal> clauses describe the <emphasis>edges</emphasis> in <emphasis>E</emphasis>. Unfortunately, such a DAG would not convey proper information to derive a real-time schedule that complies with the semantics of the OpenMP specification.</para>
<para>In order to understand where the difficulties of mapping an OpenMP program onto an expressive task graph stem from, and how to overcome them, the next section further delves into the details of the OpenMP execution model.</para>
</section>
<section class="lev2" id="sec3-3-2">
<title>3.3.2 Understanding the OpenMP Tasking Model</title>
<para>When a <literal>task</literal> construct is encountered, the execution of the new task region can be assigned to one of the threads in the current team for immediate or deferred execution, with the corresponding impact on the overall timing behaviour. Different clauses allow defining how a task, its parent task and its child tasks will behave at runtime:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>The <literal>depend</literal> clause allows describing a list of input (<literal>in</literal>), output (<literal>out</literal>), or input-output (<literal>inout</literal>) dependences on data items. Dependences can only be defined among <emphasis>sibling</emphasis> tasks, i.e., first-level descendants of the same <emphasis>parent</emphasis> task.</para></listitem>
<listitem>
<para>An <literal>if</literal> clause whose associated expression evaluates to false forces the encountering thread to suspend the current task region. Its execution cannot be resumed until the newly generated task, defined to be an <emphasis>undeferred task</emphasis>, is completed.</para></listitem>
<listitem>
<para>A <literal>final</literal> clause whose associated expression evaluates to true forces all its child tasks to be <emphasis>undeferred</emphasis> and <emphasis>included</emphasis> tasks, meaning that the encountering thread itself sequentially executes all the new descendants.</para></listitem>
<listitem>
<para>By default, OpenMP tasks are <emphasis>tied</emphasis> to the thread that first starts their execution. If such tasks are suspended, they can only be resumed by the same thread. An <literal>untied</literal> clause forces the task not to be tied to any thread; hence, in case it is suspended, it can later be resumed by any thread in the current team.</para></listitem>
</itemizedlist>
<para>Listing 3.2 shows an example of an OpenMP program using different tasking features. The <literal>parallel</literal> construct creates a new team of threads (since <literal>num_threads</literal> clause is not provided, the number of threads associated is implementation defined). The <literal>single</literal> construct (line 3) generates a new task region <emphasis>T</emphasis><subscript>0</subscript>, and its execution is assigned to just one thread in the team. When the thread executing <emphasis>T</emphasis><subscript>0</subscript> encounters its child <literal>task</literal> constructs (lines 6, 14, and 19), new tasks <emphasis>T</emphasis><subscript>1</subscript>, <emphasis>T</emphasis><subscript>2</subscript>, and <emphasis>T</emphasis><subscript>3</subscript> are generated. Similarly, the thread executing <emphasis>T</emphasis><subscript>1</subscript> creates task <emphasis>T</emphasis><subscript>4</subscript> (line 9).</para>
<table-wrap position="float" id="L3-2">
<label>Listing 3.2</label>
<caption><para>OpenMP example of task scheduling clauses</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/list3-2.jpg"/>
</table-wrap>
<para>Tasks <emphasis>T</emphasis><subscript>1</subscript> and <emphasis>T</emphasis><subscript>2</subscript> include a <literal>depend</literal> clause both defining a dependence on the memory reference <emphasis>x</emphasis>, so <emphasis>T</emphasis><subscript>2</subscript> cannot start executing until <emphasis>T</emphasis><subscript>1</subscript> finishes. <emphasis>T</emphasis><subscript>4</subscript> is defined as an <emphasis>included task</emphasis> because its parent <emphasis>T</emphasis><subscript>1</subscript> contains a <literal>final</literal> clause that evaluates to <emphasis>true</emphasis>, so <emphasis>T</emphasis><subscript>1</subscript> is suspended until the execution of <emphasis>T</emphasis><subscript>4</subscript> finishes. All tasks are guaranteed to have completed at the <emphasis>implicit barrier</emphasis> at the end of the parallel region (line 23). Moreover, task <emphasis>T</emphasis><subscript>0</subscript> will wait on the <literal>taskwait</literal> (line 17) until tasks <emphasis>T</emphasis><subscript>1</subscript> and <emphasis>T</emphasis><subscript>2</subscript> have completed before proceeding.</para>
<para>OpenMP defines <emphasis>task scheduling points</emphasis> (TSPs) as points in the program where the encountering task can be suspended, and the hosting thread can be rescheduled to a different task. TSPs occur upon task creation and completion and at task synchronization points such as <literal>taskwait</literal> directives or explicit and implicit barriers <footnote id="fn3_1" label="1"> <para>Additional TSPs are implied at different OpenMP constructs (<literal>target</literal>, <literal>taskyield</literal>, <literal>taskgroup</literal>). See Section 2.9.5 of the OpenMP specification [25] for a complete list of task scheduling points.</para></footnote>.</para>
<para>Task scheduling points divide task regions into <emphasis>task parts</emphasis> executed uninterruptedly from start to end. Different parts of the same task region are executed in the order in which they are encountered. In the absence of task synchronization constructs, the order in which a thread executes parts of different tasks is unspecified. The example shown in <link linkend="F3-2">Figure <xref linkend="F3-2" remap="3.2"/></link> identifies the parts in which each task region is divided: <emphasis>T</emphasis><subscript>0</subscript> is composed of task parts <emphasis>tp</emphasis><subscript>00</subscript>,<emphasis>tp</emphasis><subscript>01</subscript>,<emphasis>tp</emphasis><subscript>02</subscript>,<emphasis>tp</emphasis><subscript>03</subscript>, and <emphasis>tp</emphasis><subscript>04</subscript>; <emphasis>T</emphasis><subscript>1</subscript> is composed of task parts <emphasis>tp</emphasis><subscript>10</subscript>, and <emphasis>tp</emphasis><subscript>11</subscript>; and <emphasis>T</emphasis><subscript>2</subscript>, <emphasis>T</emphasis><subscript>3</subscript>, and <emphasis>T</emphasis><subscript>4</subscript> are composed of task part <emphasis>tp</emphasis><subscript>2</subscript>,<emphasis>tp</emphasis><subscript>3</subscript>, and <emphasis>tp</emphasis><subscript>4</subscript>, respectively.</para>
<para>When a task encounters a TSP, the OpenMP runtime system may either begin the execution of a task region bound to the current team, or resume any previously suspended task region also bound to it. The order in which these actions are applied is not specified by the standard, but it is subject to the following <emphasis>task scheduling constraints</emphasis> (TSCs):</para>
<table-wrap position="float">
<table cellspacing="5" cellpadding="5" frame="none" rules="none">
<tbody>
<tr>
<td valign="top" align="left" width="10%">TSC 1:</td>
<td valign="top" align="left">An <emphasis>included</emphasis> task must be executed immediately after the task is created.</td>
</tr>
<tr>
<td valign="top" align="left">TSC 2:</td>
<td valign="top" align="left">Scheduling of new <emphasis>tied</emphasis> tasks is constrained by the set of task regions that are currently tied to the thread, and that are not suspended in a barrier region. If this set is empty, any new <emphasis>tied</emphasis> task may be scheduled. Otherwise, a new <emphasis>tied</emphasis> task may be scheduled only if all tasks in the set belong to the same task region and the new <emphasis>tied</emphasis> task is a <emphasis>child task</emphasis> of the task region.</td>
</tr>
<tr>
<td valign="top" align="left">TSC 3:</td>
<td valign="top" align="left">A dependent task shall not be scheduled until its task data dependences are fulfilled.</td>
</tr>
<tr>
<td valign="top" align="left">TSC 4:</td>
<td valign="top" align="left">When a task is generated by a construct containing an <literal>if</literal> clause for which the conditional expression evaluates to false, and the previous constraints are already met, the task is executed immediately after generation of the task.</td>
</tr>
</tbody>
</table>
</table-wrap>
</section>
<section class="lev2" id="sec3-3-3">
<title>3.3.3 OpenMP and Timing Predictability</title>
<para>The execution model of OpenMP tasks differs from the DAG model in a fundamental aspect: a node in the DAG model is a sequential operation that cannot be interrupted <footnote id="fn3_2" label="2"> <para>This assumes the execution of a single DAG program, where a node cannot be interrupted to execute other nodes of the same graph. In a multi-DAG execution model, nodes can be preempted by nodes from different DAG programs if allowed by the scheduling approach.</para></footnote>. Instead, an OpenMP task can legally contain multiple TSPs at which the task can be suspended or resumed following the TSCs.</para>
<para>Moreover, in order to correctly capture scheduling constraints of each task as defined by the OpenMP specification, a DAG-based real-time scheduling model requires to know: (1) the dependences among tasks, (2) the point in time of each TSP, and (3) the scheduling clauses associated to the task.</para>
<para>This section analyses the extraction of a DAG that represents the parallel execution of an OpenMP application upon which timing analysis can be then applied. It focuses on three key elements:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>How to reconstruct an OpenMP task graph from the analysis of the code that resembles the DAG-task structure based on TSPs.</para></listitem>
<listitem>
<para>To which elements of an OpenMP program WCET analysis must be applied.</para></listitem>
<listitem>
<para>How to schedule OpenMP tasks based on DAG-task methodologies so that TSCs are met.</para></listitem>
</orderedlist>
<section class="lev2" id="sec3-3-3-1">
<title>3.3.3.1 Extracting the DAG of an OpenMP program</title>
<para>The execution of a task part resembles the execution of a node in <emphasis>V</emphasis> , i.e., it is executed uninterrupted. To that end, OpenMP task parts, instead of tasks, can be considered as nodes in <emphasis>V</emphasis>.</para>
<para><link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link> shows the DAG (named <emphasis>OpenMP-DAG</emphasis>) corresponding to the example presented in Listing 3.2, in which task parts form the nodes in <emphasis>V</emphasis>. <emphasis>T</emphasis><subscript>0</subscript> is decomposed into task parts <emphasis>tp</emphasis><subscript>00</subscript>, <emphasis>tp</emphasis><subscript>01</subscript>, <emphasis>tp</emphasis><subscript>02</subscript>, <emphasis>tp</emphasis><subscript>03</subscript>, and <emphasis>tp</emphasis><subscript>04</subscript>, with a TSP at the end of each part caused by the task constructs <emphasis>T</emphasis><subscript>1</subscript>, <emphasis>T</emphasis><subscript>2</subscript>, and <emphasis>T</emphasis><subscript>3</subscript> for <emphasis>tp</emphasis><subscript>00</subscript>, <emphasis>tp</emphasis><subscript>01</subscript>, and <emphasis>tp</emphasis><subscript>03</subscript>, and the <literal>taskwait</literal> construct for <emphasis>tp</emphasis><subscript>02</subscript>. Similarly, <emphasis>T</emphasis><subscript>1</subscript> is decomposed into <emphasis>tp</emphasis><subscript>10</subscript> and <emphasis>tp</emphasis><subscript>11</subscript> with the TSP corresponding to the creation of task <emphasis>T</emphasis><subscript>4</subscript> at the end of <emphasis>tp</emphasis><subscript>10</subscript>.</para>
<fig id="F3-5" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link></label>
<caption><para>OpenMP-DAG composed of <emphasis>task parts</emphasis> based on the code in Listing 3.2.</para></caption>
<graphic xlink:href="graphics/ch03_dag.jpg"/>
</fig>
<para>Depending on the origin of the TSP encountered at the end of each task part (i.e., task creation or completion, or task synchronization) three different types of dependences are identified: (a) control-flow dependences (dotted arrows), which force parts to be scheduled in the same order as they are executed within the task; (b) TSP dependences (dashed arrows), which force tasks to start/resume execution after the corresponding TSP, and (c) full synchronizations (solid arrows), which force the sequential execution of tasks as defined by the <literal>depend</literal> clause and task synchronization constructs. Note that all dependence types have the same purpose, which is to express a scheduling precedence constraint. As a result, the OpenMP-DAG does not require to differentiate them.</para>
<para>Besides the <literal>depend</literal> clause, the <literal>if</literal> and <literal>final</literal> clauses also affect the order in which task parts are executed. In both cases the encountering task is suspended until the newly generated task completes execution. In order to model the undeferred and included tasks behaviour, a new edge is introduced in <emphasis>E</emphasis>. In <link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link>, a new dependence between <emphasis>tp</emphasis><subscript>40</subscript> and <emphasis>tp</emphasis><subscript>11</subscript> is inserted, so the task region <emphasis>T</emphasis><subscript>1</subscript> does not resume its execution until the included task <emphasis>T</emphasis><subscript>4</subscript> finishes.</para>
</section>
<section class="lev2" id="sec3-3-3-2">
<title>3.3.3.2 WCET analysis is applied to <emphasis>tasks</emphasis> and <emphasis>taskparts</emphasis></title>
<para>In order to comply with the DAG-model, nodes in the OpenMP-DAG must be further annotated with the WCET estimation of the corresponding task parts. By constructing the OpenMP-DAG based on the knowledge of TSPs (i.e., by considering as nodes in <emphasis>V</emphasis> only those code portions that are executed uninterruptedly from start to end) the timing analysis of each node has a WCET which is independent of any dynamic instance of the OpenMP program (i.e., how threads may be scheduled to tasks and parts therein). As a result, the timing behaviour of task parts will only be affected by concurrent accesses to shared resources [37]. It is important to remark that the WCET estimation is applied to a task when it is composed of a single task part. This is the case of <emphasis>T</emphasis><subscript>2</subscript>, <emphasis>T</emphasis><subscript>3</subscript>, and <emphasis>T</emphasis><subscript>4</subscript> from <link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link>.</para>
</section>
<section class="lev2" id="sec3-3-3-3">
<title>3.3.3.3 DAG-based scheduling must not violate the TSCs</title>
<para>When real-time scheduling techniques are applied to guarantee the timing behaviour of OpenMP applications, the semantics specified by the OpenMP TSCs must not be violated.</para>
<table-wrap position="float" id="L3-3">
<label>Listing 3.3</label>
<caption><para>Example of an OpenMP fragment of code with <emphasis>tied tasks</emphasis></para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/list3-3.jpg"/>
</table-wrap>
<para>The clauses associated to a task construct not only define precedence constraints, as shown in Section 3.3.3.1, but they also define the way in which tasks, and task parts therein, are scheduled according to the TSCs defined in Section 3.3.2. This is the case of the <literal>if</literal>, <literal>final</literal> and <literal>untied</literal> clauses, as well as the default behaviour of <emphasis>tied</emphasis> tasks. These clauses influence the order in which tasks execute and also how task parts are scheduled to threads. Regarding the latter, the restrictions imposed by TSCs are the following:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis>TSC 1</emphasis> imposes <emphasis>included</emphasis> tasks to be executed immediately by the encountering thread. In this case, the scheduling of the OpenMP-DAG has to consider both the task part that encounters it and the complete <emphasis>included</emphasis> task region as a unique unit of scheduling. In <link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link>, the former case would give <emphasis>tp</emphasis><subscript>4</subscript> the highest priority, and the latter case would consider <emphasis>tp</emphasis><subscript>10</subscript> and <emphasis>tp</emphasis><subscript>4</subscript> as a unique unit of scheduling.</para></listitem>
<listitem>
<para><emphasis>TSC 2</emphasis> does not allow scheduling new <emphasis>tied</emphasis> tasks if there are other suspended <emphasis>tied</emphasis> tasks already assigned to the same thread, and the suspended tasks are not descendants of the new task. Listing 3.3 shows a fragment of code in which this situation can occur. Let&#x02019;s assume that <emphasis>T</emphasis><subscript>1</subscript>, which is not a descendent of <emphasis>T</emphasis><subscript>3</subscript>, is executed by <emphasis>thread 1</emphasis>. When T<subscript>1</subscript> encounters the TSP of the creation of <emphasis>T</emphasis><subscript>2</subscript>, it is suspended because of TSC 4, and it cannot resume until <emphasis>T</emphasis><subscript>2</subscript> finishes. Let&#x02019;s consider that <emphasis>T</emphasis><subscript>2</subscript> is being executed by a different thread, e.g., <emphasis>thread 2</emphasis>. If <emphasis>T</emphasis><subscript>2</subscript> has not finished when the TSP of the creation of <emphasis>T</emphasis><subscript>3</subscript> is reached, then <emphasis>T</emphasis><subscript>3</subscript> cannot be scheduled on <emphasis>thread 1</emphasis> because of TSC 2, even if <emphasis>thread 1</emphasis> is idle. As a result, <emphasis>tied</emphasis> tasks constrain the scheduling opportunities of the OpenMP-DAG.</para></listitem>
<listitem>
<para><emphasis>TSC 3</emphasis> imposes tasks to be scheduled respecting their dependences. This information is already contained in the OpenMP-DAG.</para></listitem>
<listitem>
<para><emphasis>TSC 4</emphasis> states that <emphasis>undeferred</emphasis> tasks execute immediately if TSCs 1, 2, and 3 are met. Differently, <emphasis>untied</emphasis> tasks are not subject to any TSC, allowing parts of the same task to execute on different threads, so when a task is suspended, the next part to be executed can be resumed on a different thread. Therefore, one possible scheduling strategy for <emphasis>untied</emphasis> tasks that satisfies TSC 4 is not to schedule <emphasis>undeferred</emphasis> and <emphasis>untied</emphasis> task parts until <emphasis>tied</emphasis> and <emphasis>included</emphasis> tasks are assigned to a given thread. This guarantees that TSCs 1 and 2 are met. This is because task parts of <emphasis>tied</emphasis> and <emphasis>included</emphasis> tasks are bound to the thread that first started their execution, which reduces significantly their scheduling opportunities. Instead, <emphasis>untied</emphasis> and <emphasis>undeferred</emphasis> task parts have a higher degree of freedom as they can be scheduled to any thread of the team. Therefore, for the OpenMP-DAG to convey enough information to devise a TSC-compliant scheduling, each node in <emphasis>V</emphasis> must be augmented with the <emphasis>type of task</emphasis> as well (<literal>untied</literal>, <emphasis>tied</emphasis>, <emphasis>undeferred</emphasis> and <emphasis>included</emphasis>) as shown in <link linkend="F3-5">Figure <xref linkend="F3-5" remap="3.5"/></link>.</para></listitem>
</itemizedlist>
<fig id="F3-6" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-6">Figure <xref linkend="F3-6" remap="3.6"/></link></label>
<caption><para>DAG composed of task parts.</para></caption>
<graphic xlink:href="graphics/ch03_dag_schedule.jpg"/>
</fig>
<para><link linkend="F3-6">Figure <xref linkend="F3-6" remap="3.6"/></link> shows a possible schedule of task parts in Listing 3.2, assuming a work-conserving scheduling. <emphasis>T</emphasis><subscript>0</subscript> is a tied task, so all its task parts are scheduled to the same thread (<emphasis>thread 1</emphasis>). <emphasis>T</emphasis><subscript>1</subscript> is an <emphasis>untied</emphasis> task so <emphasis>tp</emphasis><subscript>10</subscript> and <emphasis>tp</emphasis><subscript>10</subscript> can execute in different threads (<emphasis>thread 1</emphasis> and <emphasis>2</emphasis> in the example). Note that <emphasis>tp</emphasis><subscript>11</subscript> does not start executing until <emphasis>tp</emphasis><subscript>4</subscript> completes due to the TSP constraint. Moreover, the execution of <emphasis>tp</emphasis><subscript>4</subscript> starts immediately after the creation of <emphasis>T</emphasis><subscript>4</subscript> on the same thread that encounters it (<emphasis>thread 2</emphasis>). Finally, <emphasis>tp</emphasis><subscript>2</subscript> and <emphasis>tp</emphasis><subscript>3</subscript> are scheduled to idle threads (<emphasis>thread 4</emphasis> and <emphasis>5</emphasis>, respectively) once all their dependences are fulfilled.</para>
<table-wrap position="float" id="L3-4">
<label>Listing 3.4</label>
<caption><para>OpenMP program using the tasking model</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/list3-4.jpg"/>
</table-wrap>
</section>
</section>
</section>
<section class="lev1" id="sec3-4">
<title>3.4 Extracting the Timing Information of an OpenMP Program</title>
<para>The extraction of an OpenMP-DAG representing the parallel execution of an OpenMP program in such a way that timing analysis can be performed, requires analyzing the OpenMP constructs included in the source code, so the nodes and edges that form the DAG can be identified. This information can be obtained</para>
<para>by means of compiler analysis techniques. Concretely, there exists two different analysis stages needed to build the OpenMP-DAG <emphasis>G = (V,E)</emphasis>:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>A <emphasis>parallel structure stage</emphasis>, in which the nodes in <emphasis>V</emphasis>, i.e., tasks parts, and edges in <emphasis>E</emphasis>, are identified based on TSPs, TSCs, and data- and control-flow information.</para></listitem>
<listitem>
<para>A <emphasis>task expansion stage</emphasis>, in which the tasks (and task parts) that will be actually instantiated at runtime are identified by expanding the control flow information extracted in the previous stage.</para></listitem>
</orderedlist>
<para>The next subsections further describe these stages. With the objective of facilitating the explanation of the compiler analysis techniques, Listing 3.4 introduces an OpenMP program that will be used for illustration purposes. The code processes the elements of a blocked 2D matrix using a <emphasis>wave-front</emphasis> parallelization strategy [38]. The <literal>parallel</literal> construct (line 1) defines a team of 8 threads. The <literal>single</literal> construct (line 3) specifies that only one thread will execute the associated code. The algorithm divides the matrix in 3 &#x000D7; 3 blocks, assigning each one to a different task. Each block [<emphasis>i,j</emphasis>] consumes the previous adjacent blocks and itself. Hence, all tasks (lines 8, 11, 14, and 17:18) have an <literal>inout</literal> dependence on the computed block [<emphasis>i,j</emphasis>]. <emphasis>T</emphasis><subscript>2</subscript> and <emphasis>T</emphasis><subscript>3</subscript> (lines 11 and 14) compute the upper and left edges, so additionally they consume the left [<emphasis>i,j</emphasis> - 1] and upper [<emphasis>i - 1,j</emphasis>] blocks, respectively. Finally, <emphasis>T</emphasis><subscript>4</subscript> (lines 17:18) computes the internal blocks, hence additionally it consumes the left [<emphasis>i - 1,j</emphasis>], upper [<emphasis>i,j - 1</emphasis>], and left-upper diagonal [<emphasis>i - 1,j - 1</emphasis>] blocks. All tasks are guaranteed to complete at the implicit barrier at the end of the parallel region (line 24).</para>
<section class="lev2" id="sec3-4-1">
<title>3.4.1 Parallel Structure Stage</title>
<para>This stage identifies the TSPs surrounding tasks parts, and the corresponding TSCs associated with each task part in order to: (1) generate a parallel control-flow graph (PCFG) that holds all this information as well as parallel semantics [39], and (2) analyze this graph so that the necessary information to expand a complete DAG is obtained. With such purpose in mind the analysis performs the following calculations:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Generate the PCFG of the source code taking into account: (a) the dependences introduced by any kind of TSPs (i.e., task creation, task completion and task synchronization), as introduced in Section 3.3.3.1, (b) the data dependences introduced by the <literal>depend</literal> clause, and (c) the <literal>if</literal> and <literal>final</literal> clauses, hence the behaviour of undeferred and included tasks.</para></listitem>
<listitem>
<para>On top of that, analyze the control-flow statements, i.e., selection statements (if-else and switch) and loops that identify whether a task is instantiated or not at runtime. To do so, three analyses are required: induction variables [40], reaching definitions [41], and range analysis [42]. Additionally, determine the conditions that must be fulfilled for two instantiated tasks to depend on one another [3].</para></listitem>
</itemizedlist>
<section class="lev2" id="sec3-4-1-1">
<title>3.4.1.1 Parallel control flow analysis</title>
<para>The <emphasis>abstract syntax tree</emphasis> (AST) used in the compiler to represent the source code is used to generate the PCFG of an OpenMP program. This enriches the classic control-flow graph (CFG) with information about parallel execution. This process performs a conservative analysis of the synchronizations among tasks, because the compiler may not be able to assert when two <literal>depend</literal> clauses designate the same memory location, e.g., array accesses or pointers. Hence, synchronization edges are augmented with predicates defining the condition to be fulfilled for an edge to exist. In the example shown in Listing 3.4, the dependences that matrix <emphasis>m</emphasis> originates among tasks depend on the values of <emphasis>i</emphasis> and <emphasis>j</emphasis>.</para>
</section>
<section class="lev2" id="sec3-4-1-2">
<title>3.4.1.2 Induction variables analysis</title>
<para>On top of the PCFG, the compiler evaluates the loop statements to discover the induction variables (IVs) and their evolution over the iterations using the common tuple representation &#x027E8;<emphasis>lb,ub,str</emphasis>&#x027E9;, where <emphasis>lb</emphasis> is the lower bound, <emphasis>ub</emphasis> is the upper bound, and <emphasis>str</emphasis> is the stride. This analysis is essential for the later expansion of the graph, since the induction variables will determine the shape of the iteration space for each loop statement.</para>
</section>
<section class="lev2" id="sec3-4-1-3">
<title>3.4.1.3 Reaching definitions and range analysis</title>
<para>Finally, the compiler computes the values of all variables involved in the execution of any task. With such a purpose, it analyzes reaching definitions and also extends range analysis with support for OpenMP. The former computes the definitions reaching any point in the program. The later computes the values of the variables at any point of the program in four steps: (1) generate a set &#x1D49E; of equations that constrain the values of each variable (equations are built for each assignment and control flow statement); (2) build a <emphasis>constraint graph</emphasis> that represents the relations among the constraints; (3) split the graph into <emphasis>strongly connected components</emphasis> (SCCs) to avoid cycles; (4) propagate the ranges over the SCCs in topological order. Both analyses are needed to propagate the values of the relevant variables across the expanded code.</para>
</section>
<section class="lev2" id="sec3-4-1-4">
<title>3.4.1.4 Putting all together: The wave-front example</title>
<para>The previously mentioned analyses provide the information needed to generate an initial version of the DAG, named <emphasis>augmented DAG</emphasis> (<emphasis>aDAG</emphasis>), with data and control flow knowledge. The aDAG is defined by the tuple</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq3-1.jpg"/></para>
<para>where:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis>N = &#x0007B;V &#x000D7; T<subscript>N</subscript>&#x0007D;</emphasis> is the set of nodes with their corresponding type <emphasis>T<subscript>N</subscript> = &#x0007B;Task,Taskwait,Barrier&#x0007D;</emphasis>.</para></listitem>
<listitem>
<para><emphasis>E = &#x0007B;N &#x000D7; N &#x000D7; P&#x0007D;</emphasis> is the set of possible synchronization edges with the predicate P that must fulfill for the edge to exist.</para></listitem>
<listitem>
<para><emphasis>C = N &#x000D7;&#x0007B;F&#x0007D;</emphasis> is the set of <emphasis>control flow statements</emphasis> involved in the instantiation of any task <emphasis>n &#x02208; N</emphasis>, where <emphasis>F = S &#x000D7;&#x0007B;T<subscript>F</subscript>&#x0007D;</emphasis>, being <emphasis>S</emphasis> the condition to instantiate the tasks and <emphasis>T<subscript>F</subscript> = &#x0007B;Loop,IfElse,Switch&#x0007D;</emphasis>, the type of the structure.</para></listitem>
</itemizedlist>

<para><link linkend="F3-7">Figure <xref linkend="F3-7" remap="3.7"/></link> shows the aDAG of the OpenMP program in Listing 3.4. The set of nodes <emphasis>N</emphasis> includes all task constructs <emphasis>N = T</emphasis><subscript>1</subscript>, <emphasis>T</emphasis><subscript>2</subscript>, <emphasis>T</emphasis><subscript>3</subscript>, <emphasis>T</emphasis><subscript>4</subscript> (lines 8, 11, 14, and 17:18), all with type <emphasis>T<subscript>N</subscript> = Task</emphasis>. The control flow statements for each node <emphasis>N, f<subscript>i</subscript> &#x02208; F</emphasis> are the <literal>for</literal> (lines 5 and 6) and <literal>if</literal> (lines 7, 10, 13, and 16) statements, and include information about: (a) the IVs of each loop <emphasis>i,j</emphasis>, both with <emphasis>lb</emphasis> = 0, <emphasis>ub</emphasis> = 2 and <emphasis>str</emphasis> = 1 (dashed-line boxes); (b) the conditions of the selection statements enclosing each task (solid-line boxes), and (c) the ranges of the variables in those conditions. In the figure, <emphasis>T</emphasis><subscript>3</subscript> is instantiated if <emphasis>i</emphasis> = 1 or 2 and <emphasis>j</emphasis> = 0. In the predicates ap &#x02208; P associated to the synchronization edges in <emphasis>E</emphasis>, the left hand side of the equality corresponds to the value of the variable at the point in time the source task is instantiated, while the right side corresponds to the value when the target task is instantiated. For example, the predicate of the edge between <emphasis>T</emphasis><subscript>1</subscript> and <emphasis>T</emphasis><subscript>3</subscript> with <emphasis>p1((i<subscript>S</subscript> == i<subscript>T</subscript>&#x0007C;&#x0007C;i<subscript>S</subscript> == i<subscript>T</subscript> - 1)&#x00026;&#x00026;j<subscript>S</subscript> == j<subscript>T</subscript>)</emphasis> evaluates to <emphasis>true</emphasis>, meaning that the edge exists when the values of <emphasis>i</emphasis> and <emphasis>j</emphasis> in the source task <emphasis>T</emphasis><subscript>1</subscript> are <emphasis>i<subscript>S</subscript> = 0</emphasis> and <emphasis>j<subscript>S</subscript> = 0</emphasis>, and the values of <emphasis>i</emphasis> and <emphasis>j</emphasis> in the target task <emphasis>T</emphasis><subscript>3</subscript> are <emphasis>i<subscript>T</subscript></emphasis> = 1 and <emphasis>j<subscript>T</subscript></emphasis> = 0.</para>
<fig id="F3-7" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-7">Figure <xref linkend="F3-7" remap="3.7"/></link></label>
<caption><para>aDAG of the OpenMP program in Listing 3.4.</para></caption>
<graphic xlink:href="graphics/ch03_aDAG.jpg"/>
</fig>
<para>For simplicity, <link linkend="F3-7">Figure <xref linkend="F3-7" remap="3.7"/></link> only includes the dependences that are actually expanded in the next stage (Section 3.4.2). The actual aDAG has edges between any possible pair of tasks because they all have <literal>inout</literal> dependences on the element <emphasis>m[i][j]</emphasis>. Moreover, the task-parts that form the task <emphasis>T</emphasis><subscript>0</subscript> with the corresponding task creation dependences are not included.</para>
</section>
</section>
<section class="lev2" id="sec3-4-2">
<title>3.4.2 Task Expansion Stage</title>
<section class="lev2" id="sec3-4-2-1">
<title>3.4.2.1 Control flow expansion and synchronization predicate resolution</title>
<para>Based on the aDAG, this stage generates an <emphasis>expanded DAG</emphasis> (or simply DAG) representing the complete execution of the program in two phases: (1) expand control flow structures (i.e., decide which branches are taken for the selection statements and how many iterations are executed for the loop statements) to determine which tasks (and so task-parts) are actually instantiated; and (2) resolve the synchronization predicates to conclude which tasks have actual dependences.</para>
<para>Control flow structures are expanded from outer to inner levels. In the aDAG in <link linkend="F3-7">Figure <xref linkend="F3-7" remap="3.7"/></link>, the outer loop <emphasis>f</emphasis><subscript>1</subscript> is expanded first, and then the inner loop <emphasis>f</emphasis><subscript>2</subscript>. Finally, the if-else structures <emphasis>f</emphasis><subscript>3</subscript>, <emphasis>f</emphasis><subscript>4</subscript>, <emphasis>f</emphasis><subscript>5</subscript>, and <emphasis>f</emphasis><subscript>6</subscript> are resolved. Each expansion requires the evaluation of the associated expressions to determine the values of each variable. For example, when the outer loop <emphasis>f</emphasis><subscript>1</subscript> is expanded, each iteration is associated with the corresponding value of <emphasis>i</emphasis>.</para>
<para>This expansion process creates two identifiers: (1) an identifier of the loops involved in the creation of a task (<emphasis>l<subscript>i</subscript></emphasis>), labeling each loop expansion step, and (2) a unique <emphasis>static task construct identifier</emphasis> (<emphasis>sid<subscript>t</subscript></emphasis>), labeling each <literal>task</literal> construct.</para>
<para>The process results in a temporary DAG in which all tasks instantiated at runtime are defined, but synchronization predicates are not solved. To do so, the value of the variables propagated in the control flow expansion is used to evaluate predicates and decide which edges actually exist. Likewise, loop identifiers <emphasis>l<subscript>i</subscript></emphasis> are used to eliminate backwards dependences, i.e., tasks instantiated in previous iterations cannot depend on tasks instantiated in later iterations.</para>
<para><link linkend="F3-8">Figure <xref linkend="F3-8" remap="3.8"/></link> shows the final DAG of the program in Listing 3.4. It contains all task instances with a unique numerial identifier (explained in the next section) and all dependences that can potentially exist at runtime. Transitive dependences (dashed arrows) are included as well, although they can be removed because they are redundant.</para>
<fig id="F3-8" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F3-8">Figure <xref linkend="F3-8" remap="3.8"/></link></label>
<caption><para>The DAG of the OpenMP program in Listing 3.4.</para></caption>
<graphic xlink:href="graphics/ch03_eDAG.jpg"/>
</fig>
</section>
<section class="lev2" id="sec3-4-2-2">
<title>3.4.2.2 <emphasis>t<subscript>id</subscript></emphasis>: A unique task instance identifier</title>
<para>A key property of the expanded task instances is that they must include a <emphasis>unique task instance identifier t<subscript>id</subscript></emphasis> required to match the instantiated tasks expanded at compile-time (and included in the DAG) with those instantiated at runtime. Equation 3.2 computes <emphasis>t<subscript>id</subscript></emphasis> as follows:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq3-2.jpg"/></para>
<para>where <emphasis>sid<subscript>t</subscript></emphasis> is a unique task construct identifier (computed during the control flow expansion stage), <emphasis>T</emphasis> is equal to the number of <literal>task</literal>, <literal>taskwait</literal>, and <literal>barrier</literal> constructs in the source code, <emphasis>L<subscript>t</subscript></emphasis> is the total number of nested loops involved in the execution of the task <emphasis>t, i</emphasis> refers to the the nesting level, <emphasis>l<subscript>i</subscript></emphasis> is the loop unique identifier at nesting level <emphasis>i</emphasis> (computed during the control flow expansion stage), and <emphasis>M</emphasis> the maximum number of iterations of any considered loop.</para>
<para>The use of loop properties in Equation 3.2 (i.e., <emphasis>L<subscript>t</subscript>, l<subscript>i</subscript>, i</emphasis>, and <emphasis>M</emphasis>), guarantees that a unique task identifier for each task instance is generated, even if they come from the same task construct. Hence, task instances from different loop iterations result in different <emphasis>t<subscript>id</subscript></emphasis> because every nesting level <emphasis>l<subscript>i</subscript></emphasis> is multiplied by the maximum number of iterations <emphasis>M</emphasis>.</para>
<para>Consider task <emphasis>T</emphasis><subscript>4</subscript>, with identifier 79, in <link linkend="F3-8">Figure <xref linkend="F3-8" remap="3.8"/></link>. This task instance corresponds to the computation of the matrix block <emphasis>m</emphasis>[2,1]. Its identifier is computed as follows: (1) <emphasis>sid</emphasis><subscript><emphasis>T</emphasis>4</subscript> = 4, because <emphasis>T</emphasis><subscript>4</subscript> is the fourth task found in sequential order while traversing the source code; (2) <emphasis>T</emphasis> = 5 because there are four task constructs and one (implicit) barrier in the source code; (3) <emphasis>L</emphasis><subscript><emphasis>T</emphasis><subscript>4</subscript></subscript> = 2, the two nested loops enclosing <emphasis>T</emphasis><subscript>4</subscript>; (4) <emphasis>M</emphasis> = 3, the maximum number of iterations in any of the two considered loops; and (5) <emphasis>l</emphasis><subscript>1</subscript> = 2 and <emphasis>l</emphasis><subscript>2</subscript> = 1 are the values of the loop identifiers at the corresponding iteration. Putting them all together: <emphasis>T</emphasis><subscript>4<subscript><emphasis>id</emphasis></subscript></subscript> = 4 + 5(2 &#x02217; 3<superscript>1</superscript> + 1 &#x02217; 3<superscript>2</superscript>) = 79.</para>
<para>It is important to remark that <emphasis>t<subscript>id</subscript></emphasis> must be computed at both compile-time and run-time, and so all information needed to compute Equation 3.2 must be available in both places. <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link> presents the combined compiler and run-time mechanisms needed to reproduce all the required information (including <emphasis>sid<subscript>t</subscript></emphasis> and <emphasis>l<subscript>i</subscript></emphasis> identifiers) at run-time.</para>
</section>
<section class="lev2" id="sec3-4-2-3">
<title>3.4.2.3 Missing information when deriving the DAG</title>
<para>In case the framework cannot derive some information (mostly when control-flow statements and dependences contain pointers that may alias or arrays with unresolved subscripts, or the values are not known at compile-time), it still generates a DAG that correctly represents the execution of the program. Next, each possible case is argued:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>When an if-else statement cannot be evaluated, all its related tasks in <emphasis>C</emphasis> are considered for instantiation, hence included in the DAG. In this case, the DAG will include a task instance that will never exist. Chapters 4 and 6 present the mechanisms required to take this into consideration for response time analysis and parallel run-time execution.</para></listitem>
<listitem>
<para>If a loop cannot be expanded because its boundaries are unknown, parallelism across iterations is disabled by inserting a <literal>taskwait</literal> at the end of the loop. By doing so, all tasks instantiated within an iteration must complete before the next iteration starts.</para></listitem>
<listitem>
<para>Lastly, dependences whose predicate cannot be evaluated are always kept in the DAG, making the involved tasks serialized.</para></listitem>
</itemizedlist>
<para>The situations described above will result in a bigger DAG (when if&#x02013;else conditions cannot be evaluated) or in a performance loss (when loop bounds or synchronization predicates cannot be determined), although a correct DAG is guaranteed. In the worst-case scenario, where no information can be derived at compile-time, the resultant DAG corresponds to the sequential execution of the program, i.e., all tasks are assumed to be instantiated, and their execution is to be sequentialized. It is important to remark that embedded applications can often provide all the required information to complete the DAG expansion, as it is required for timing analysis [43].</para>
</section>
</section>
<section class="lev2" id="sec3-4-3">
<title>3.4.3 Compiler Complexity</title>
<para>The complexity of the compiler is determined by the complexity of the two stages presented in Sections 3.4.1 and 3.4.2.</para>
<para>The complexity of the control/data flow analysis stage is dominated by the PCFG analysis and range analysis phases. The complexity of the former is related to the number of split constructs present in the source code, in which the Cyclomatic Complexity [44] metric is usually used. The latter, has been proved to have an asymptotic linear complexity [42].</para>
<para>The complexity of the task expansion stage is dominated by the computation of the dependences among tasks, which is performed using a Cartesian product: the input dependence of a task can be generated by any of the previously created task instances. As a result, the complexity is quadratic on the number of instantiated tasks.</para>
</section>
</section>
<section class="lev1" id="sec3-5">
<title>3.5 Summary</title>
<para>This chapter provided the rationale and the model for the use of fine-grained parallelism in general, and the OpenMP parallel programming model in particular, to support applications that require predictable performance, to develop future critical real-time embedded systems, and analyze the time predictable properties of the OpenMP tasking model. Based on this model, the chapter then described the advances in compiler techniques to extract timing information of OpenMP parallel programs, and build the <emphasis>OpenMP</emphasis> DAG required to enable predictable scheduling (described in the next chapter) and the needed timing analysis (in <link linkend="ch05">Chapter <xref linkend="ch5" remap="5"/></link>). This OpenMP-DAG also provides the building block for the execution of the OpenMP runtime (<link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link>) and Operating System (<link linkend="ch07">Chapter <xref linkend="ch7" remap="7"/></link>).</para>
</section>
<section class="lev1" id="sec3-6">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Pllana, S., and Xhafa, F., <emphasis>Programming Multicore and Many-core Computing Systems</emphasis>, volume 86. John Wiley and Sons, 2017. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Pllana%2C+S%2E%2C+and+Xhafa%2C+F%2E%2C+Programming+Multicore+and+Many-core+Computing+Systems%2C+volume+86%2E+John+Wiley+and+Sons%2C+2017%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Reinders, J., <emphasis>Intel Threading Building Blocks</emphasis>. O&#x02019;Reilly and Associates, Inc., 2007. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Reinders%2C+J%2E%2C+Intel+Threading+Building+Blocks%2E+O%27Reilly+and+Associates%2C+Inc%2E%2C+2007%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>NVIDIA CUDA C Programming Guide.</emphasis> <ulink url="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html">https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html</ulink>, 2016.</para></listitem>
<listitem>
<para>Stone, J. E., Gohara, D., and Shi, G., OpenCL: A parallel programming standard for heterogeneous computing systems. <emphasis>CSE</emphasis>, 12, 66&#x02013;73, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Stone%2C+J%2E+E%2E%2C+Gohara%2C+D%2E%2C+and+Shi%2C+G%2E%2C+OpenCL%3A+A+parallel+programming+standard+for+heterogeneous+computing+systems%2E+CSE%2C+12%2C+66-73%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Snir, M.,  <emphasis>MPI&#x02013;the Complete Reference: The MPI core</emphasis>, volume 1. MIT press, 1998. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Snir%2C+M%2E%2C++MPI-the+Complete+Reference%3A+The+MPI+core%2C+volume+1%2E+MIT+press%2C+1998%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Butenhof, D. R., <emphasis>Programming with POSIX Threads</emphasis>. Addison-Wesley, 1997. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Butenhof%2C+D%2E+R%2E%2C+Programming+with+POSIX+Threads%2E+Addison-Wesley%2C+1997%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Chapman, B., Jost, G., and Van Der Pas., <emphasis>Using OpenMP: Portable Shared Memory Parallel Programming</emphasis>, volume 10. MIT press, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Chapman%2C+B%2E%2C+Jost%2C+G%2E%2C+and+Van+Der+Pas%2E%2C+Using+OpenMP%3A+Portable+Shared+Memory+Parallel+Programming%2C+volume+10%2E+MIT+press%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Duran, A., Ayguad&#x000E9;, E., Badia, R. M., Labarta, J., Martinell, L., Martorell, X., and Planas, J., Ompss: a proposal for programming heterogeneous multi-core architectures. <emphasis>Parallel Process. Lett.</emphasis> 21, 173&#x02013;193, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Duran%2C+A%2E%2C+Ayguad%E9%2C+E%2E%2C+Badia%2C+R%2E+M%2E%2C+Labarta%2C+J%2E%2C+Martinell%2C+L%2E%2C+Martorell%2C+X%2E%2C+and+Planas%2C+J%2E%2C+Ompss%3A+a+proposal+for+programming+heterogeneous+multi-core+architectures%2E+Parallel+Process%2E+Lett%2E+21%2C+173-193%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Varbanescu, A. L., Hijma, P., Van Nieuwpoort, R., and Bal, H., &#x0201C;Towards an effective unified programming model for many-cores.&#x0201D; In <emphasis>IPDPS</emphasis>, pp. 681&#x02013;692. IEEE, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Varbanescu%2C+A%2E+L%2E%2C+Hijma%2C+P%2E%2C+Van+Nieuwpoort%2C+R%2E%2C+and+Bal%2C+H%2E%2C+%22Towards+an+effective+unified+programming+model+for+many-cores%2E%22+In+IPDPS%2C+pp%2E+681-692%2E+IEEE%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>OpenACC. <emphasis>Directives for Accelerators</emphasis>. <ulink url="http://www.openacc-standard.org">http://www.openacc-standard.org</ulink>, 2017.</para></listitem>
<listitem>
<para>Robison, A. D., Cilk plus: Language support for thread and vector parallelism. <emphasis>Talk at HP-CAST</emphasis>, 18:25, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Robison%2C+A%2E+D%2E%2C+Cilk+plus%3A+Language+support+for+thread+and+vector+parallelism%2E+Talk+at+HP-CAST%2C+18%3A25%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Leiserson, C. E., &#x0201C;The cilk++ concurrency platform.&#x0201D; In <emphasis>Design Automation Conference, 2009. DAC&#x02019;09. 46th ACM/IEEE</emphasis>, pp. 522&#x02013;527. IEEE, 2009.</para></listitem>
<listitem>
<para>Saule, E., and &#x000C7;ataly&#x000FC;rek, &#x000DC;. V., &#x0201C;An early evaluation of the scalability of graph algorithms on the intel mic architecture.&#x0201D; In <emphasis>Parallel and Distributed Processing Symposium Workshops and PhD Forum (IPDPSW), 2012 IEEE 26th International</emphasis>, pp. 1629&#x02013;1639. IEEE, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Saule%2C+E%2E%2C+and+%C7ataly%FCrek%2C+%DC%2E+V%2E%2C+%22An+early+evaluation+of+the+scalability+of+graph+algorithms+on+the+intel+mic+architecture%2E%22+In+Parallel+and+Distributed+Processing+Symposium+Workshops+and+PhD+Forum+%28IPDPSW%29%2C+2012+IEEE+26th+International%2C+pp%2E+1629-1639%2E+IEEE%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>De Dinechin, B. D., Van Amstel, D., Poulhi&#x000E9;s, M., and Lager, G., &#x0201C;Time-critical computing on a single-chip massively parallel processor.&#x0201D; In <emphasis>DATE</emphasis>, 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=De+Dinechin%2C+B%2E+D%2E%2C+Van+Amstel%2C+D%2E%2C+Poulhi%E9s%2C+M%2E%2C+and+Lager%2C+G%2E%2C+%22Time-critical+computing+on+a+single-chip+massively+parallel+processor%2E%22+In+DATE%2C+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>CEA STMicroelectronics. Platform 2012: A many-core programmable accelerator for ultra-efficient embedded computing in nanometer technology. <emphasis>Whitepaper,</emphasis> 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=CEA+STMicroelectronics%2E+Platform+2012%3A+A+many-core+programmable+accelerator+for+ultra-efficient+embedded+computing+in+nanometer+technology%2E+Whitepaper%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Texas Instruments. <emphasis>SPRS866: 66AK2H12/06 Multicore DSP+ARM KeyStone II System-on-Chip (SoC)</emphasis>.</para></listitem>
<listitem>
<para>Kegel, P., Schellmann, M., and Gorlatch, S., &#x0201C;<emphasis>Using OpenMP vs. Threading Building Blocks for Medical Imaging on Multi-Cores</emphasis>.&#x0201D; In <emphasis>Europar</emphasis>. Springer, 2009. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Kegel%2C+P%2E%2C+Schellmann%2C+M%2E%2C+and+Gorlatch%2C+S%2E%2C+%22Using+OpenMP+vs%2E+Threading+Building+Blocks+for+Medical+Imaging+on+Multi-Cores%2E%22+In+Europar%2E+Springer%2C+2009%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Lee, S., Min, S-J., and Eigenmann, R., OpenMP to GPGPU: A Compiler Framework for Automatic Translation and Optimization. <emphasis>SIGPLAN Not.</emphasis> 44, 101&#x02013;110, 2009. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lee%2C+S%2E%2C+Min%2C+S-J%2E%2C+and+Eigenmann%2C+R%2E%2C+OpenMP+to+GPGPU%3A+A+Compiler+Framework+for+Automatic+Translation+and+Optimization%2E+SIGPLAN+Not%2E+44%2C+101-110%2C+2009%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Shen, J., Fang, J., Sips, H., and Varbanescu, A. L., &#x0201C;Performance gaps between OpenMP and OpenCL for multi-core CPUs.&#x0201D; In <emphasis>ICPPW</emphasis>, pp. 116&#x02013;125. IEEE, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Shen%2C+J%2E%2C+Fang%2C+J%2E%2C+Sips%2C+H%2E%2C+and+Varbanescu%2C+A%2E+L%2E%2C+%22Performance+gaps+between+OpenMP+and+OpenCL+for+multi-core+CPUs%2E%22+In+ICPPW%2C+pp%2E+116-125%2E+IEEE%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Krawezik, G., and Cappello, F., <emphasis>&#x0201C;</emphasis>Performance comparison of MPI and three OpenMP programming styles on shared memory multiprocessors.&#x0201D; In <emphasis>SPAA</emphasis>. ACM, 2003. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Krawezik%2C+G%2E%2C+and+Cappello%2C+F%2E%2C+%22Performance+comparison+of+MPI+and+three+OpenMP+programming+styles+on+shared+memory+multiprocessors%2E%22+In+SPAA%2E+ACM%2C+2003%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Kuhn, B., Petersen, P., and O&#x02019;Toole, E., OpenMP versus threading in C/C++. <emphasis>Concurr. Pract. Exp.</emphasis> 12, 1165&#x02013;1176, 2000. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Kuhn%2C+B%2E%2C+Petersen%2C+P%2E%2C+and+O%27Toole%2C+E%2E%2C+OpenMP+versus+threading+in+C%2FC%2B%2B%2E+Concurr%2E+Pract%2E+Exp%2E+12%2C+1165-1176%2C+2000%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>OpenMP 2.5 Application Programming Interface</emphasis>. <ulink url="http://www.openmp.org/wp-content/uploads/spec25.pdf">http://www.openmp.org/wp-content/uploads/spec25.pdf</ulink>, 2005.</para></listitem>
<listitem>
<para><emphasis>OpenMP 3.0 Application Programming Interface</emphasis>. <ulink url="http://www.openmp.org/wp-content/uploads/spec30.pdf">http://www.openmp.org/wp-content/uploads/spec30.pdf</ulink>, 2008.</para></listitem>
<listitem>
<para><emphasis>OpenMP 4.0 Application Programming Interface</emphasis>. <ulink url="http://www.openmp.org/wp-content/uploads/OpenMP4.0.0.pdf">http://www.openmp.org/wp-content/uploads/OpenMP4.0.0.pdf</ulink>, 2013.</para></listitem>
<listitem>
<para><emphasis>OpenMP 4.5 Application Programming Interface.</emphasis> <ulink url="http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf">http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf</ulink>, 2015.</para></listitem>
<listitem>
<para><emphasis>OpenMP Technical Report 2 on the OMPT Interface.</emphasis> <ulink url="http://www.openmp.org/wp-content/uploads/ompt-tr2.pdf">http://www.openmp.org/wp-content/uploads/ompt-tr2.pdf</ulink>, 2014.</para></listitem>
<listitem>
<para><emphasis>OpenMP Technical Report 4: Version 5.0 Preview 1</emphasis>. <ulink url="http://www.openmp.org/wp-content/uploads/openmp-tr4.pdf">http://www.openmp.org/wp-content/uploads/openmp-tr4.pdf</ulink>, 2016.</para></listitem>
<listitem>
<para><emphasis>OpenMP Technical Report 5: Memory Management Support for OpenMP 5.0</emphasis>. <ulink url="http://www.openmp.org/wp-content/uploads/openmp-TR5-final.pdf">http://www.openmp.org/wp-content/uploads/openmp-TR5-final.pdf</ulink>, 2017.</para></listitem>
<listitem>
<para>Podobas, A., and Karlsson, S., &#x0201C;Towards Unifying OpenMP Under the Task-Parallel Paradigm.&#x0201D; In <emphasis>IWOMP</emphasis>, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Podobas%2C+A%2E%2C+and+Karlsson%2C+S%2E%2C+%22Towards+Unifying+OpenMP+Under+the+Task-Parallel+Paradigm%2E%22+In+IWOMP%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Buttazzo, G. C., <emphasis>Hard Real-Time Computing Systems: Predictable Scheduling Algorithms and Applications</emphasis>, volume 24. Springer Science and Business Media, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Buttazzo%2C+G%2E+C%2E%2C+Hard+Real-Time+Computing+Systems%3A+Predictable+Scheduling+Algorithms+and+Applications%2C+volume+24%2E+Springer+Science+and+Business+Media%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Davis, R. I., and Burns, A., A survey of hard real-time scheduling for multiprocessor systems. <emphasis>ACM computing surveys (CSUR)</emphasis>, 43, 35, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Davis%2C+R%2E+I%2E%2C+and+Burns%2C+A%2E%2C+A+survey+of+hard+real-time+scheduling+for+multiprocessor+systems%2E+ACM+computing+surveys+%28CSUR%29%2C+43%2C+35%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Bonifaci, V., Marchetti-Spaccamela, A., Stiller, S., and Wiese, A., &#x0201C;Feasibility analysis in the sporadic dag task model.&#x0201D; In <emphasis>Real-Time Systems (ECRTS), 2013 25th Euromicro Conference on</emphasis>, pp. 225&#x02013;233. IEEE, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Bonifaci%2C+V%2E%2C+Marchetti-Spaccamela%2C+A%2E%2C+Stiller%2C+S%2E%2C+and+Wiese%2C+A%2E%2C+%22Feasibility+analysis+in+the+sporadic+dag+task+model%2E%22+In+Real-Time+Systems+%28ECRTS%29%2C+2013+25th+Euromicro+Conference+on%2C+pp%2E+225-233%2E+IEEE%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Baruah, S., Bonifaci, V., Marchetti-Spaccamela, A., Stougie, L., and Wiese, A., &#x0201C;A generalized parallel task model for recurrent real-time processes.&#x0201D; In <emphasis>Real-Time Systems Symposium (RTSS), 2012 IEEE 33rd</emphasis>, pp. 63&#x02013;72. IEEE, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Baruah%2C+S%2E%2C+Bonifaci%2C+V%2E%2C+Marchetti-Spaccamela%2C+A%2E%2C+Stougie%2C+L%2E%2C+and+Wiese%2C+A%2E%2C+%22A+generalized+parallel+task+model+for+recurrent+real-time+processes%2E%22+In+Real-Time+Systems+Symposium+%28RTSS%29%2C+2012+IEEE+33rd%2C+pp%2E+63-72%2E+IEEE%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Saifullah, A., Li, J., Agrawal, K., Lu, C., and Gill, C., Multi-core real-time scheduling for generalized parallel task models. <emphasis>Real-Time Sys.</emphasis> 49, 404&#x02013;435, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Saifullah%2C+A%2E%2C+Li%2C+J%2E%2C+Agrawal%2C+K%2E%2C+Lu%2C+C%2E%2C+and+Gill%2C+C%2E%2C+Multi-core+real-time+scheduling+for+generalized+parallel+task+models%2E+Real-Time+Sys%2E+49%2C+404-435%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Baruah, S., &#x0201C;Improved multiprocessor global schedulability analysis of sporadic dag task systems.&#x0201D; In <emphasis>Real-Time Systems (ECRTS), 2014 26th Euromicro Conference on</emphasis>, pp. 97&#x02013;105. IEEE, 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Baruah%2C+S%2E%2C+%22Improved+multiprocessor+global+schedulability+analysis+of+sporadic+dag+task+systems%2E%22+In+Real-Time+Systems+%28ECRTS%29%2C+2014+26th+Euromicro+Conference+on%2C+pp%2E+97-105%2E+IEEE%2C+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Li, J., Agrawal, K., Lu, C., and Gill, C., &#x0201C;Outstanding paper award: Analysis of global edf for parallel tasks.&#x0201D; In <emphasis>Real-Time Systems (ECRTS), 2013 25th Euromicro Conference on</emphasis>, pp. 3&#x02013;13. IEEE, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Li%2C+J%2E%2C+Agrawal%2C+K%2E%2C+Lu%2C+C%2E%2C+and+Gill%2C+C%2E%2C+%22Outstanding+paper+award%3A+Analysis+of+global+edf+for+parallel+tasks%2E%22+In+Real-Time+Systems+%28ECRTS%29%2C+2013+25th+Euromicro+Conference+on%2C+pp%2E+3-13%2E+IEEE%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Radojkovi&#x00107;, P., Girbal, S., Grasset, A., Quiones, E., Yehia, S., and Cazorla, F. J., On the evaluation of the impact of shared resources in multithreaded cots processors in time-critical environments. <emphasis>ACM Trans. Architec. Code Opt. (TACO)</emphasis>, 8:34, 2012. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Radojkovi&#x0107;%2C+P%2E%2C+Girbal%2C+S%2E%2C+Grasset%2C+A%2E%2C+Quiones%2C+E%2E%2C+Yehia%2C+S%2E%2C+and+Cazorla%2C+F%2E+J%2E%2C+On+the+evaluation+of+the+impact+of+shared+resources+in+multithreaded+cots+processors+in+time-critical+environments%2E+ACM+Trans%2E+Architec%2E+Code+Opt%2E+%28TACO%29%2C+8%3A34%2C+2012%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Rochange, C., Bonenfant, A., Sainrat, P., Gerdes, M., Wolf, J., Ungerer, T., et al. &#x0201C;Wcet analysis of a parallel 3d multigrid solver executed on the merasa multi-core.&#x0201D; In <emphasis>OASIcs-OpenAccess Series in Informatics</emphasis>, volume 15. Schloss Dagstuhl-Leibniz-Zentrum fuer Informatik, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Rochange%2C+C%2E%2C+Bonenfant%2C+A%2E%2C+Sainrat%2C+P%2E%2C+Gerdes%2C+M%2E%2C+Wolf%2C+J%2E%2C+Ungerer%2C+T%2E%2C+%22Wcet+analysis+of+a+parallel+3d+multigrid+solver+executed+on+the+merasa+multi-core%2E%22+In+OASIcs-OpenAccess+Series+in+Informatics%2C+volume+15%2E+Schloss+Dagstuhl-Leibniz-Zentrum+fuer+Informatik%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Royuela, S., Ferrer, R., Caballero, D., and Martorell, X., &#x0201C;Compiler analysis for openmp tasks correctness.&#x0201D; In <emphasis>Proceedings of the 12th ACM International Conference on Computing Frontiers</emphasis>, p. 7. ACM, 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Royuela%2C+S%2E%2C+Ferrer%2C+R%2E%2C+Caballero%2C+D%2E%2C+and+Martorell%2C+X%2E%2C+%22Compiler+analysis+for+openmp+tasks+correctness%2E%22+In+Proceedings+of+the+12th+ACM+International+Conference+on+Computing+Frontiers%2C+p%2E+7%2E+ACM%2C+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Muchnick, S. S., <emphasis>Advanced Compiler Design Implementation</emphasis>. Morgan Kaufmann, 1997. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Muchnick%2C+S%2E+S%2E%2C+Advanced+Compiler+Design+Implementation%2E+Morgan+Kaufmann%2C+1997%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Aho, A. V., Sethi, R., and Ullman, J. D., <emphasis>Compilers: Principles, Techniques, and Tools</emphasis>, volume 2. Addison-wesley Reading, 2007.</para></listitem>
<listitem>
<para>Pereira, F. M. Q., Rodrigues, R. E., and Campos, V. H. S., &#x0201C;A fast and low-overhead technique to secure programs against integer overflows.&#x0201D; In <emphasis>Proceedings of the 2013 IEEE/ACM International Symposium on Code Generation and Optimization (CGO)</emphasis>, pp 1&#x02013;11. IEEE Computer Society, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Pereira%2C+F%2E+M%2E+Q%2E%2C+Rodrigues%2C+R%2E+E%2E%2C+and+Campos%2C+V%2E+H%2E+S%2E%2C+%22A+fast+and+low-overhead+technique+to+secure+programs+against+integer+overflows%2E%22+In+Proceedings+of+the+2013+IEEE%2FACM+International+Symposium+on+Code+Generation+and+Optimization+%28CGO%29%2C+pp+1-11%2E+IEEE+Computer+Society%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Wilhelm, R., Engblom, J., Ermedahl, A., Holsti, N., Thesing, S., Whalley, D., et al. The worst-case execution-time problem?&#x0201D;overview of methods and survey of tools. <emphasis>ACM Trans. Embed. Comput. Sys. (TECS)</emphasis>, 7:36, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Wilhelm%2C+R%2E%2C+Engblom%2C+J%2E%2C+Ermedahl%2C+A%2E%2C+Holsti%2C+N%2E%2C+Thesing%2C+S%2E%2C+Whalley%2C+D%2E%2C+The+worst-case+execution-time+problem%B4%22overview+of+methods+and+survey+of+tools%2E+ACM+Trans%2E+Embed%2E+Comput%2E+Sys%2E+%28TECS%29%2C+7%3A36%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>McCabe, T. J., &#x0201C;A complexity measure.&#x0201D; <emphasis>IEEE Transactions on software Engineering</emphasis>, 4, pp. 308&#x02013;320, 1976. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=McCabe%2C+T%2E+J%2E%2C+%22A+complexity+measure%2E%22+IEEE+Transactions+on+software+Engineering%2C+4%2C+pp%2E+308-320%2C+1976%2E" target="_blank">Google Scholar</ulink></para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch04" label="4" xreflabel="4">
<title>Mapping, Scheduling, and Schedulability Analysis</title>
<para><emphasis role="strong">Paolo Burgio<superscript><emphasis role="strong">1</emphasis></superscript>, Marko Bertogna<superscript><emphasis role="strong">1</emphasis></superscript>, Alessandra Melani<superscript><emphasis role="strong">1</emphasis></superscript>, Eduardo Qui&#x000F1;ones<superscript><emphasis role="strong">2</emphasis></superscript> and Maria A. Serrano<superscript><emphasis role="strong">2</emphasis></superscript></emphasis></para>
<para><superscript>1</superscript>University of Modena and Reggio Emilia, Italy<?lb?><superscript>2</superscript>Barcelona Supercomputing Center (BSC), Spain</para>
<para>This chapter presents how the P-SOCRATES framework addresses the issue of scheduling multiple real-time tasks (RT tasks), made of multiple and concurrent non-preemptable <emphasis>task parts</emphasis>. In its most generic form, the scheduling problem in the architectural framework is a dual problem: scheduling task-to-threads, and scheduling thread-to-core replication.</para>
<section class="lev1" id="sec4-1">
<title>4.1 Introduction</title>
<para>In our framework, we assume threads in the same OpenMP application are statically <emphasis>pinned</emphasis> to the available cores in the platforms <footnote id="fn4_1" label="1"> <para>Still, to enable multitasking at the OS level, the OS can preempt threads from one OpenMP application in favour of another OpenMP application.</para></footnote>. This approach has two advantages: (i) the lower layer of the software stack, namely the runtime and the operating system (OS) support, are much simpler to design and implement; and (ii) we remove one dimension from the scheduling problem, that is, we only need to solve the problem of assigning tasks (in our case, OpenMP task parts) to threads/cores. For this reason, and limited to this chapter, we use the words &#x0201C;mapping&#x0201D; and &#x0201C;scheduling&#x0201D; interchangeably. As explained in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>, when a task encounters a task scheduling point (TSP), program execution branches into the OpenMP runtime, where task-to-thread mapping can: (1) begin the execution of a task region bound to the current team or (2) resume any previously suspended task region bound to the current team, as defined by the <emphasis>parallel</emphasis> OpenMP construct. Note that, the order in which these two actions are applied is not specified by the standard. An ideal task scheduler will schedule tasks for execution in a way that maximizes concurrency while accounting for load imbalance and locality to facilitate better performance.</para>
<para>The following part of the chapter describes the design of a simple partitioned scheduler, detailing how to enforce a limited-preemption scheduling policy to limit the overhead related to context switches whenever higher-priority instances arrive while the cores are busy executing lower-priority workload. It is also called <emphasis>static</emphasis> approach.</para>
<para>Then, we introduce the so-called dynamic approach, where scheduling happens with the adoption of a global queue where all tasks are inserted, and from where they can potentially be fetched by any worker in the system. We also show how it can be enhanced to support task migration across computing threads and cores, in a work-conservative environment.</para>
<para>In the following part, we describe our overall framework for the schedulability analysis, and then we specialize it for static/partitioned approach and dynamic/global approach, respectively.</para>
<para>We then briefly discuss the scheduling problem in the multi-core system that powers the four I/O clusters present in the fabric.</para>
</section>
<section class="lev1" id="sec4-2">
<title>4.2 System Model</title>
<para>In the framework, an application may consist of multiple RT task instances, each one characterized by a different period or minimum inter-arrival time, deadline and execution requirement (see <link linkend="F4-1">Figure <xref linkend="F4-1" remap="4.1"/></link>). Each RT task starts executing on the host processor and may include (OpenMP-compliant) parallel workloads to be offoaded to the many-core accelerator. Such a parallel workload needs then to be scheduled on the available processing elements (PEs).</para>
<fig id="F4-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-1">Figure <xref linkend="F4-1" remap="4.1"/></link></label>
<caption><para>An application is composed of multiple real-time tasks.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_1.jpg"/>
</fig>
<para>The parallel execution of each RT task is represented by a direct acyclic graph (DAG) composed of a set of nodes representing task parts. Nodes are connected through edges that represent precedence constraints among different task parts of the same offoad. A task part can be executed only if all nodes that have a precedence constraint over it have already been executed.</para>
<para>To comply with the OpenMP semantics, an RT task is not directly scheduled on the PEs. Instead, its parallel workload is first mapped to several OS threads (up to the number of PEs available), and then these OS threads are scheduled onto the available cores. <link linkend="F4-2">Figure <xref linkend="F4-2" remap="4.2"/></link> summarizes the mapping/scheduling framework. Here, only partitioned/static approach is shown, where there is one task queue for each worker thread. In a fully dynamic/global approach, there is only one queue for every RT task, where all threads push and fetch work.</para>
<fig id="F4-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-2">Figure <xref linkend="F4-2" remap="4.2"/></link></label>
<caption><para>RT tasks are mapped to OS threads, which are scheduled on the processing elements.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_2.jpg"/>
</fig>
<para>The number of OS threads onto which an RT task is mapped depends on mapping decisions. If the RT task does not present a large parallelism, it makes no sense to map it onto more than a limited number of threads. If instead the RT task has massively parallel regions, it may be useful to map it to a higher number of threads, up to the number of PEs in the many-core accelerator. A correct decision should consider the trade-off between the OS overhead implied by many threads and the speed-up obtainable with a larger run-time parallelism. Note that creating a larger number of threads than necessary may impose a significant burden to the OS, which needs to maintain the context of these threads, schedule, suspend, and resume them, with an obvious increase in the system overhead.</para>
<para>The architectural template targeted in the project (described in <link linkend="ch02">Chapter <xref linkend="ch2" remap="2"/></link>) is a many-core platform where cores are grouped onto clusters. The testbed accelerator, Kalray MPPA of the &#x0201C;Bostan&#x0201D; generation, has 256 cores grouped into 16 clusters of 16 cores each. We consider only the threads offoaded to the same cluster. Note that the intra-cluster scheduling problem is the main problem to solve in our scheduling framework. The reason is that P-SOCRATES adopts an execution model, where the context of each RT task that may need to be accelerated is statically offoaded to the target clusters before runtime.</para>
<para>For the above reasons, the main problem is therefore how to efficiently activate and schedule the threads associated with the different RT tasks that have been offoaded to the same cluster. The threads of each RT task will contend to execute on the available PEs with the threads of the other RT tasks. A smart scheduler will therefore need to decide which thread, or set of threads, to execute at any time in each PE of the considered cluster, such that all scheduling constraints are met. Depending on the characteristics of the running RT tasks (priority, period, deadline, etc.) the scheduler may choose to preempt an executing thread or set of threads, to schedule a different set of threads belonging to a higher priority (or more urgent) RT task.</para>
</section>
<section class="lev1" id="sec4-3">
<title>4.3 Partitioned Scheduler</title>
<para>In a traditional partitioned scheduler, OS threads are statically assigned to cores, so that no thread may migrate from one core to another. The scheduling problem then reduces to the design of a single-core scheduler. We start from this approach and design our task-to-thread scheduler.</para>
<section class="lev2" id="sec4-3-1">
<title>4.3.1 The Optimality of EDF on Preemptive Uniprocessors</title>
<para>The earliest deadline first (EDF) scheduling algorithm assigns scheduling priority to jobs according to their absolute deadlines: the earlier the deadline, the greater the priority (with ties broken arbitrarily). EDF is known to be <emphasis>optimal</emphasis> for scheduling a collection of jobs upon a preemptive uniprocessor platform, in the sense that <emphasis>if a given collection of jobs can be scheduled to meet all deadlines, then the EDF-generated schedule for this collection of jobs will also meet all deadlines</emphasis> [1]. To show that a system is EDF-schedulable upon a preemptive uniprocessor, it suffices to show the existence of a schedule meeting all deadlines &#x02014; the optimality of EDF ensures that it will find such a schedule. Unfortunately, most of the commercial RTOSes do not implement the EDF scheduling policy. The main reasons are found in the added complexity of the scheduler, requiring timers to keep track of the thread deadlines, and in the agnostic behavior with respect to higher-priority workload. This last concern is particularly important for industrial applications that have a set of higher-priority instances whose execution cannot be delayed. With an EDF scheduler, a lower-priority instance overrunning its expected budget may end up causing a deadline miss of a higher priority instance that has a later deadline. Instead, with a Fixed Priority (FP) scheduler, higher-priority jobs are protected against lower-priority overruns, because they will always be able to preempt a misbehaving lower-priority instance. This makes FP scheduling more robust for mixed-criticality scenarios where RT tasks of different criticality may contend for the same PEs. For the importance of FP scheduling, we decided to implement a partitioned scheduler based on this policy.</para>
</section>
<section class="lev2" id="sec4-3-2">
<title>4.3.2 FP-scheduling Algorithms</title>
<para>In an FP-scheduling algorithm, each thread is assigned a distinct priority (as in P-SOCRATES scheduling model) and every instance (a.k.a. job/RT task instance) released by the thread inherits the priority of the associated thread.</para>
<para>The rate-monotonic (RM) scheduling algorithm [1] is an FP-scheduling algorithm in which the priorities of the tasks are defined based on their period: tasks with a smaller period are assigned greater priority (with ties broken arbitrarily). It is known [1] that RM is an optimal FP-scheduling algorithm for scheduling threads with relative deadlines equal to their minimum inter-arrival times upon preemptive uniprocessors: if there is any FP-scheduling algorithm that can schedule a given set of implicit-deadline threads to always meet all deadlines of all jobs, then RM will also always meet all deadlines of all jobs.</para>
<para>The deadline monotonic (DM) scheduling algorithm [2] is another FP-scheduling algorithm in which the priority of a task is defined based on its relative deadline parameter rather than its period: threads with smaller relative deadlines are assigned greater priority (with ties broken arbitrarily). Note that RM and DM are equivalent for implicit deadline systems, since all threads in such systems have their relative deadline parameters equal to their periods. It has been shown in [2] that DM is an optimal FP-scheduling algorithm for scheduling sets of constrained-deadline threads upon preemptive uniprocessors: if there is any FP-scheduling algorithm that can schedule a given constrained-deadline system to always meet all deadlines of all jobs, then DM will also always meet all deadlines of all jobs. DM is, however, known to not be optimal for systems where threads may have a deadline larger than their period.</para>
</section>
<section class="lev2" id="sec4-3-3">
<title>4.3.3 Limited Preemption Scheduling</title>
<para>Preemption is a key concept in real-time scheduling, since it allows the OS to immediately allocate the processor to threads requiring urgent service. In fully preemptive systems, the running thread can be interrupted at any time by another thread with higher priority and be resumed to continue when all higher priority threads have completed. In other systems, preemption may be disabled for certain intervals of time during the execution of critical operations (e.g., interrupt service routines, critical sections, etc.). In other situations, preemption can be completely forbidden to avoid unpredictable interference among threads and achieve a higher degree of predictability (although higher blocking times).</para>
<para>The question of whether to enable or disable preemption during thread execution has been investigated by many authors under several points of view and it is not trivial to answer. A general disadvantage of the non-preemptive discipline is that it introduces additional blocking time in higher-priority threads, thereby reducing schedulability. On the other hand, preemptive scheduling may add a significant overhead due to context switches, significantly increasing the worst-case execution time. Both situations are schematized in <link linkend="F4-3">Figure <xref linkend="F4-3" remap="4.3"/></link>. CRPD in the figure stands for Cache-Related Preemption Delay, that is, the time overhead added to tasks&#x02019; execution time due to cache cooling after a preemption.</para>
<fig id="F4-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-3">Figure <xref linkend="F4-3" remap="4.3"/></link></label>
<caption><para>Fully preemptive vs. non-preemptive scheduling: preemption overhead and blocking delay may cause deadline misses.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_3.jpg"/>
</fig>
<para>There are several advantages to be considered when adopting a non-preemptive scheduler. Arbitrary preemptions can introduce a significant runtime overhead and may cause high fluctuations in thread-execution times, which degrades system predictability. Specifically, at least four different types of costs need to be considered at each preemption:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis>Scheduling cost</emphasis>: It is the time taken by the scheduling algorithm to suspend the running thread, insert it into the ready queue, switch the context, and dispatch the new incoming thread.</para></listitem>
<listitem>
<para><emphasis>Pipeline cost</emphasis>: It accounts for the time taken to flush the processor pipeline when the thread is interrupted, and the time taken to refill the pipeline when the thread is resumed.</para></listitem>
<listitem>
<para><emphasis>Cache-related cost</emphasis>: It is the time taken to reload the cache lines evicted by the preempting thread. The WCET increment due to cache interference can be very large with respect to the WCET measured in non-preemptive mode.</para></listitem>
<listitem>
<para><emphasis>Bus-related cost</emphasis>: It is the extra bus interference for accessing the next memory level due to the additional cache misses caused by preemption.</para></listitem>
</orderedlist>
<para>In order to predictably bound these penalties without sacrificing schedulability, we decided to adopt a limited preemption scheduler, which represents a trade-off between fully preemptive and non-preemptive scheduling. Note that this seamlessly integrates into the standard OpenMP tasking/execution model, where tasks can be preempted only at well-defined TSPs. See also <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>.</para>
</section>
<section class="lev2" id="sec4-3-4">
<title>4.3.4 Limited Preemption Schedulability Analysis</title>
<para>As in the fully preemptive case, the schedulability analysis of limited preemptive scheduling can be done analyzing the critical instant that leads to the worst-case response time of a given thread. However, differently from the fully preemptive case, the critical instant is not given by the synchronous arrival sequence, where all threads arrive at the same time, and all successive instances are released as soon as possible. Instead, in the presence of non-preemptive regions, the additional blocking from lower priority threads must be taken into account. Hence, the critical instant for a thread <emphasis>&#x003C4;<subscript>i</subscript></emphasis> occurs when it is released synchronously and periodically with all higher priority threads, while the lower priority thread that is responsible of the largest blocking time of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is released one unit of time before <emphasis>&#x003C4;<subscript>i</subscript></emphasis>.</para>
<para>However, the largest response time of a thread is not necessarily due to the first job after a critical instant but might be due to later jobs. Therefore, as shown in [3], the schedulability analysis needs to check all <emphasis>&#x003C4;<subscript>i</subscript></emphasis>&#x02019;s jobs within a given period of interest that goes from the above described critical instant until the first idle instant of <emphasis>&#x003C4;<subscript>i</subscript></emphasis>. Let <emphasis>K<subscript>i</subscript></emphasis> be the number of such jobs.</para>
<para>When analyzing the schedulability of limited preemptive systems, a key role is played by the last non-preemptive region. Let <emphasis>q<subscript>i</subscript><superscript>last</superscript></emphasis> be the length of the last non-preemptive region of thread <emphasis>&#x003C4;<subscript>i</subscript></emphasis>. When such a value is large, the response time of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> may decrease because the execution of many higher-priority instances is postponed after the end of <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, thus not interfering with <emphasis>&#x003C4;<subscript>i</subscript></emphasis>. This allows improving the schedulability over the fully preemptive approach.</para>
<para>The blocking tolerance <emphasis>&#x003B2;<subscript>i</subscript></emphasis> of thread <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is defined as the maximum blocking that <emphasis>&#x003C4;<subscript>i</subscript></emphasis> can tolerate without missing its deadline. Such a value may be computed by the following pseudo-polynomial relation:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg70-1.jpg"/></para>
<para>where &#x003A0;<subscript><emphasis role="rm">i,k</emphasis></subscript> is the set of release times of jobs within the period of interest. The maximum allowed non-preemptive region of a &#x003C4;<subscript><emphasis>k</emphasis></subscript> is then given by:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg70-2.jpg"/></para>
<para>Such a value determines the maximum spacing between two consecutive preemption points for each thread <emphasis>&#x003C4;<subscript>k</subscript></emphasis>.</para>
</section>
</section>
<section class="lev1" id="sec4-4">
<title>4.4 Global Scheduler with Migration Support</title>
<section class="lev2" id="sec4-4-1">
<title>4.4.1 Migration-based Scheduler</title>
<para>The scheduling problem for single-core systems has already been solved with optimal priority assignments and scheduling algorithms back in the 1970s. In particular, RM assigning priorities with decreasing task periods, and DM assigning priorities with decreasing relative deadlines, are optimal priority assignments for sporadic systems with, respectively, implicit and constrained deadlines. This means that if a sporadic or synchronous periodic task system can be scheduled with fixed priorities on a single processor, then it can also be scheduled using RM (for implicit deadlines) [4] or DM (for constrained deadlines) [2]. Also, the EDF &#x02014; that schedules at each time-instant the ready job with the earliest absolute deadline &#x02014; is an optimal scheduling algorithm for scheduling arbitrary collections of jobs on a single processor [3, 4]. Therefore, if it is possible to schedule a set of jobs such that all deadlines are met, then the same collection of jobs can be successfully scheduled by EDF as well. These observations allowed us to optimally select the scheduling policies for the partitioned scheduler that we will describe shortly.</para>
<para>When allowing tasks to migrate among different cores, such as in the case of OpenMP untied task model (see <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link> for further information), things are much more complicated: EDF, RM, and DM are no more optimal and can fail even at very low utilizations (arbitrarily close to one) due to the so-called Dhall&#x02019;s effect [5]. Still, these are unlucky corner cases which do not often recur in practice. The alternative approaches that allow higher schedulability ratios are dynamic algorithms that however lead to a higher number of preemptions and migrations, allowing the priority of a job to change multiple times. Examples are Pfair [6, 7], BF [8], LLREF [9], EKG [10], E-TNPA [11], LRE-TL [12], DP-fair [13], BF<superscript>2</superscript> [14, 15], and RUN [16]. The optimality of the above algorithms holds under very restrictive circumstances, i.e., neglecting preemption and migration overhead, and for sequential sporadic tasks with implicit deadlines. In this case, they are able to reach a full schedulable utilization, equal to the number of processors. Instead, they are not optimal when tasks may have deadlines different from periods (it has been shown in [17] that an optimal scheduler would require clairvoyance), for more general task models including parallel regions, limited preemptions, and/or DAG-structures, as with the task models adopted in the P-SOCRATES project.</para>
<para>The additional complexity inherent to the implementation, runtime overhead, scheduling and schedulability analysis of dynamic scheduling algorithms, as well as in the lack of optimality properties with relation to the task model adopted in the project, made their applicability to the considered setting questionable. For this reason, we decided to opt for the static priority class of scheduling algorithms, which is far more used in a practical setting due to some particularly desired features. Systems scheduled with static priority algorithms are rather easy to implement and to analyze; they allow reducing the response time of more critical tasks by increasing their priorities; they have a limited number of preemptions (and therefore migrations), bounded by the number of jobs activations in a given interval; they allow selectively refining the scheduling of the system by simply modifying the priority assignment, without needing to change the core of the scheduling algorithm (a much more critical component); they are easier to debug, simplifying the understanding of system monitoring traces and making it more intuitive to figure out why/when each task executes on which core; they are far more composable, i.e., changing any timing parameter of a lower-priority task does not alter the schedule of a higher-priority one, avoiding the need to recheck and re-validate the whole system.</para>
</section>
<section class="lev2" id="sec4-4-2">
<title>4.4.2 Putting All Together</title>
<para>In our scheduling framework, the global scheduler will therefore consist of a fixed-priority scheduling algorithm. Each RT task is assigned a fixed priority, which is inherited by each one of its threads (there are at most <emphasis>m</emphasis> threads for each RT task). Threads that are <emphasis>ready</emphasis> to execute are ordered according to their priority in a global queue (&#x0201C;ready queue&#x0201D;) from which the scheduler selects the <emphasis>m</emphasis> highest priority ones for execution, being <emphasis>m</emphasis> the number of available cores. These executing threads are popped from the queue and they change their state to <emphasis>running</emphasis>. New thread activations and incoming offoads are queued in the ready queue, based on their priorities. A blocked queue is also maintained with all suspended or waiting threads. Whenever a waiting thread is awakened, e.g., because the condition it was waiting for was satisfied, it is removed from the blocked queue and re-inserted into the ready queue according to its priority.</para>
<para>If the newly activated thread has a priority higher than one of the m <emphasis>running</emphasis> tasks, a preemption may take place, depending on the adopted preemption policy. With a fully preemptive scheduler, the preemption takes place immediately, as soon as the thread is (re-)activated. With a non-preemptive policy, the preemption is postponed until one of the running tasks finishes its execution. For this framework, we decided to adopt a limited preemption policy. According to this policy, threads are non-preemptively executed until they reach one of the statically defined preemption points, where they can be preempted if a higher priority thread is waiting to execute. This policy allows decreasing the preemption and migration overhead of fully preemptive policies, without imposing the excessive blocking delays experienced with non-preemptive approaches.</para>
<para>The problem with adopting a limited preemption scheduling policy is that it is necessary to define at which points to allow a preemption for each thread. Since requiring the programmer to manually insert suitable context-switch locations overly increases the programming complexity, we decided to automate the process by using meaningful information coming from the OpenMP mapping layer. In particular, the concept of TSP will be exposed to the scheduling layer in order to take informed decisions on when and where to allow a preemption. We will now detail this strategy.</para>
</section>
<section class="lev2" id="sec4-4-3">
<title>4.4.3 Implementation of a Limited Preemption Scheduler</title>
<para>Arbitrary preemptions can introduce a significant runtime overhead and high fluctuations in thread-execution times, which degrades system predictability. These variations are due to multiple factors, including the time taken by the scheduling algorithm to suspend the running thread, insert it into the ready queue, switch the context, and dispatch the new incoming thread; the time taken to reload the cache lines evicted by the preempting thread; and the extra bus interference for accessing the next memory level due to the additional cache misses caused by preemption. Conversely, completely forbidding preemptions may cause an intolerable blocking to higher priority threads, potentially affecting their schedulability. For example, consider a system where a low-priority activity offoaded a parallel workload executing on all available cores. If a higher priority RT task now requests a subset of the cores to execute more important activities, it will need to wait until the low-priority ones are finished, eventually leading to a deadline miss. Such a miss could have been easily avoided by allowing preemptions.</para>
<para>With the limited preemption scheduling model adopted in the project, threads will execute non-preemptively until they reach a TSP. At these points, the execution control is moved back to the OpenMP runtime to decide which task (part) to map on that thread. Essentially, the mapper will fetch one of the tasks (belonging to the offoad associated to the considered thread) that are ready to execute and map it to that thread. These are points that mark an interruption in the task-execution flow, potentially leading to context switches and/or some memory locality loss. In other words, TSPs are good candidate to be selected for potential preemption points, since they may represent a discontinuity in the continuous execution of a task, potentially requiring a new task to load new data to local memory. Taking advantage of these points seems reasonable to guarantee a reduced pollution of cache locality of an executing task, allowing a thread context switch only when a preemption causes less harm.</para>
<para>However, it remains to be shown how the information from the OpenMP runtime is to be propagated to the RTOS scheduling layer. Note that every RT task that is offoaded to the accelerator is managed by an instance of custom OpenMP runtime. This instance, among the other tasks, keeps track of the dependencies among the nodes of the RT task (the OpenMP task parts), and schedules for execution only those nodes whose dependencies have been satisfied. When a thread fetches a task from the pool for execution, it will continue uninterruptedly until it reaches a TSP. At this point, the runtime regains control, and it may decide to invoke the OS scheduler using a simple function call. The scheduler can then check whether there are new offoad requests pending and/or there are blocked tasks that have been awakened. Potential higher-priority threads arrivals will then trigger a preemption, saving the context of the preempted thread and scheduling the higher priority one.</para>
<para>In this way, the OpenMP semantics of TSPs are propagated at RTOS scheduling level, allowing smarter decisions on the preemption locations. The timing analysis will also be significantly easier, since it will be sufficient to analyze the worst-case execution requirements of each task part, knowing that such code blocks will be executed without interruptions. The timing characterization of each task part will factor in the worst-case delay related to interfering instances, assuming each task part needs to (re-)load all required data from scratch. This makes the analysis robust and tractable, without requiring the timing analyzer to consider all possible instructions as potential preemption points but characterizing only the worst-case timing parameters of each individual task part. In <link linkend="ch05">Chapter <xref linkend="ch5" remap="5"/></link>, it is described how to obtain the maximum execution time of a task part, with and without including the additional time-penalty due to interference with other applications running concurrently. These two timing estimates are added to the characterization of every task part in the TDG produced by the compiler. This new TDG annotated with timing information is called the OpenMP-TDG and serves as input to our schedulability analysis.</para>
<para>Still, one may further reduce the number of potential preemption points, by not invoking the OS scheduler at each TSP. For example, with a Breadth-First mapping model, a task creating additional tasks will continue executing on its thread, without leading to a (task-level) context switch. In this case, it may be better not to invoke the OS scheduler at TSPs coinciding with task-creation directives, since the original task may continue executing without any discontinuity in the local context. A smarter option can be to invoke the OS scheduler only when the runtime decides to map a different task next (e.g., because the current one is finished, or due to a work-first strategy, or because of a taskwait directive). These TSPs are more likely to lead to a cache locality loss, reducing the additional impact due to preemptions.</para>
<para>That said, in order to simplify the schedulability analysis and avoid long non-preemptive regions, we decided to invoke the scheduler at each TSP. Although it may be beneficial to reduce the number of preemption points, we opted for the simplest solution that allows us to provide a proof of concept of the proposed approach. In the evaluation phase, we will then identify the impact of the preemption points to the scheduling overhead.</para>
</section>
</section>
<section class="lev1" id="sec4-5">
<title>4.5 Overall Schedulability Analysis</title>
<para>We now will describe the overall schedulability analysis of systems executing within the P-SOCRATES framework. The analysis is based on the computation of the worst-case response time of RT tasks concurrently executing on a given cluster of cores. Two different analyses are presented, depending on the mapping/scheduling mechanisms supported by the framework: (i) a dynamic solution based on a global scheduler allowing a work-conserving behavior, and (ii) a fully static solution based on a partitioned scheduler and a fixed task-to-thread mapping.</para>
<section class="lev2" id="sec4-5-1">
<title>4.5.1 Model Formalization</title>
<para>On our overall framework, an OpenMP program is composed of recurring instances of a RT task (identified with a <emphasis>target</emphasis> OpenMP construct), which in turn is composed of task parts. Without loss of generality, in this paragraph, we consider [18] a set &#x003C4; = &#x0007B;&#x003C4;<subscript>1</subscript>,&#x02026;,&#x003C4;<subscript><emphasis role="rm">n</emphasis></subscript>&#x0007D; of <emphasis>n</emphasis> sporadic conditional parallel tasks (cp-tasks) that execute upon a platform consisting of <emphasis>m</emphasis> identical processors. Each cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> releases a potentially infinite sequence of jobs. Each job of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is separated from the next by at least T<subscript><emphasis role="rm">k</emphasis></subscript> time-units and has a constrained relative deadline D<subscript><emphasis role="rm">k</emphasis></subscript> &#x0003C;=<emphasis role="rm"> T</emphasis><subscript><emphasis role="rm">k</emphasis></subscript>. Moreover, each cp-task &#x003C4;<subscript><emphasis role="rm">k</emphasis></subscript> is represented as a directed acyclic graph G<subscript><emphasis role="rm">k</emphasis></subscript> = (V <subscript><emphasis role="rm">k</emphasis></subscript>, E<subscript><emphasis role="rm">k</emphasis></subscript>), where V<subscript><emphasis role="rm">k</emphasis></subscript> = &#x0007B;v<subscript><emphasis role="rm">k,1</emphasis></subscript>,&#x02026;,v<subscript><emphasis role="rm">k,nk</emphasis></subscript>&#x0007D; is a set of nodes (or vertices) and E<subscript><emphasis role="rm">k</emphasis></subscript> is a set of directed arcs (or edges), as shown in <link linkend="F4-4">Figure <xref linkend="F4-4" remap="4.4"/></link>. Each node v<subscript><emphasis role="rm">k,j</emphasis></subscript> represents a sequential chunk of execution (or &#x0201C;sub-task&#x0201D;) and is characterized by a worst-case execution time C<subscript><emphasis role="rm">k,j</emphasis></subscript>. Preemption and migration overhead is assumed to be integrated within the WCET values, as given by the timing analysis. Arcs represent dependencies between sub-tasks, that is, an edge (v<subscript><emphasis role="rm">k,1</emphasis></subscript>, v<subscript><emphasis role="rm">k,2</emphasis></subscript>) means that v<subscript><emphasis role="rm">k,1</emphasis></subscript> must complete before v<subscript><emphasis role="rm">k,2</emphasis></subscript> can start executing. A node with no incoming arcs is referred to as a source, while a node with no outgoing arcs is referred to as a sink. Without loss of generality, each cp-task is assumed to have exactly one source v<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">source</emphasis></superscript> and one sink node v<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">sink</emphasis></superscript>. If this is not the case, a dummy source/sink node with zero WCET can be added to the DAG, with arcs to/from all the source/sink nodes. The subscript <emphasis>k</emphasis> in the parameters associated with the task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is omitted whenever the reference to the task is clear in the discussion.</para>
<fig id="F4-4" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-4">Figure <xref linkend="F4-4" remap="4.4"/></link></label>
<caption><para>A sample cp-task. Each vertex is labeled with the WCET of the corresponding sub-task.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_4.jpg"/>
</fig>
<para>In the cp-task model, nodes can be of two types:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Regular nodes, represented as rectangles, allow all successor nodes to be executed in parallel;</para></listitem>
<listitem>
<para>Conditional nodes, coming in pairs and denoted by diamonds and circles, represent the beginning and the end of a conditional construct, respectively, and require the execution of exactly one node among the successors of the start node.</para></listitem>
</orderedlist>
<para>Please note that this is a general solution for scheduling parallel recurring RT-Dags. In the specific domain of this project, where OpenMP is used as a frontend to specify DAGS, it may occur that the compiler cannot fully extract the DAG because there are conditionals that cannot be statically solved. See Section 3.4.3.2, &#x0201C;Missing information of the DAG&#x0201D;, in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>, for a discussion about this issue.</para>
<para>To properly model the possible execution flows, a further restriction is imposed to the connections within a conditional branch. That is, a node belonging to a branch of a conditional statement cannot be connected to nodes outside that branch (including other branches of the same statement). This is formally stated in the following definition.</para>
<para><emphasis role="strong">Definition 4.1</emphasis>. Let (v<subscript>1</subscript>, v<subscript>2</subscript>) be a pair of conditional nodes in a DAG G<subscript>k</subscript> = (V<subscript>k</subscript>, E<subscript>k</subscript>). The pair (v<subscript>1</subscript>, v<subscript>2</subscript>) is a conditional pair if the following holds:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>If there are exactly q outgoing arcs from v<subscript>1</subscript> to nodes s<subscript>1</subscript>, s<subscript>2</subscript>, &#x02026;, s<subscript><emphasis>q</emphasis></subscript>, for some q > 1, then there are exactly q incoming arcs into v<subscript>2</subscript> in E<subscript><emphasis>k</emphasis></subscript>, from some nodes t<subscript>1</subscript>, t<subscript>2</subscript>,&#x02026;, t<subscript><emphasis>q</emphasis></subscript>.</para></listitem>
<listitem>
<para>For each l &#x1D716;&#x0007B;1,2,&#x02026;,<emphasis>q</emphasis>&#x0007D;, let V<subscript><emphasis>l</emphasis></subscript>&#x02019;E<subscript><emphasis>l</emphasis></subscript>&#x02019; denote all the nodes and arcs on paths reachable from sl that do not include node v<subscript>2</subscript>. By definition, s<subscript><emphasis>l</emphasis></subscript> is the sole source node of the DAG Gl&#x02019;:= (V <subscript><emphasis>l</emphasis></subscript>&#x02019;E<subscript><emphasis>l</emphasis></subscript>&#x02019;). It must hold that tl is the sole sink node of G<subscript><emphasis>l</emphasis></subscript>&#x02019;.</para></listitem>
<listitem>
<para>It must hold that V<subscript><emphasis>l</emphasis></subscript>&#x02019; and V<subscript><emphasis>j</emphasis></subscript>&#x02019; have a null intersection, for all <emphasis>l</emphasis> &#x02260; <emphasis>j</emphasis>. Additionally, with the exception of (v<subscript>1</subscript>, s<subscript><emphasis>l</emphasis></subscript>) there should be no arcs in E<subscript><emphasis>k</emphasis></subscript> into nodes in V<subscript><emphasis>l</emphasis></subscript>&#x02019; from nodes not in V<subscript><emphasis>l</emphasis></subscript>&#x02019;, for each l in &#x0007B;1,2,&#x02026;,<emphasis>q</emphasis>&#x0007D;.</para></listitem>
</orderedlist>
<para>A chain or path of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is a sequence of nodes &#x003BB; = (v<subscript><emphasis role="rm">k,a</emphasis></subscript>,&#x02026;,v<subscript><emphasis role="rm">k,b</emphasis></subscript>) such that (v<subscript><emphasis role="rm">k,j</emphasis></subscript>,v<subscript><emphasis role="rm">k,j+1</emphasis></subscript>)&#x1D716;E<subscript><emphasis role="rm">k</emphasis></subscript>, for all j &#x1D716; [a,b]. The length of a chain of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, denoted by len(&#x003BB;), is the sum of the WCETs of all its nodes. The longest path of a cp-task is any source-sink path of the task that achieves the longest length.</para>
<para><emphasis role="strong">Definition 4.2</emphasis>. The length of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, denoted by L<subscript><emphasis>k</emphasis></subscript>, is the length of any longest path of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>.</para>
<para>Note that L<subscript><emphasis>k</emphasis></subscript> also represents the minimum worst-case execution time of cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, that is, the time required to execute it when the number of processing units is sufficiently large (potentially infinite) to allow the task to always execute with maximum parallelism. A necessary condition for the feasibility of a cp-task <emphasis>&#x003C4;<subscript><emphasis>k</emphasis></subscript></emphasis> is that L<subscript><emphasis>k</emphasis></subscript> &#x02264; D<subscript>k</subscript>.</para>
<para>In the absence of conditional branches, the classical sporadic DAG task model defines the volume of the task as the worst-case execution time needed to complete it on a dedicated single-core platform. This quantity can be computed as the sum of the WCETs of all the sub-tasks, that is &#x02211;<emphasis><subscript>v<subscript>k,j</subscript>&#x02208;V <subscript>k</subscript></subscript>C<subscript>k,j</subscript></emphasis>. In the presence of conditional branches, assuming that all sub-tasks are always executed is overly pessimistic. Hence, the concept of volume of a cp-task is generalized by introducing the notion of worst-case workload.</para>
<para><emphasis role="strong">Definition 4.3</emphasis>. The worst-case workload W<subscript><emphasis>k</emphasis></subscript> of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is the maximum time needed to execute an instance of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> on a dedicated single-core platform, where the maximum is taken among all possible choices of conditional branches.</para>
<para>Section 4.5 will explain in detail how the worst-case workload of a task can be computed efficiently.</para>
<para>The utilization U<subscript><emphasis role="rm">k</emphasis></subscript> of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is the ratio between its worst-case workload and its period, that is, U<subscript><emphasis role="rm">k</emphasis></subscript> = W<subscript><emphasis role="rm">k</emphasis></subscript>/T<subscript><emphasis role="rm">k</emphasis></subscript>. For the task-set &#x003C4;, its total utilization U is defined as the sum of the utilizations of all tasks. A simple necessary condition for feasibility is U <emphasis>&#x02264; m</emphasis>.</para>
<para><link linkend="F4-4">Figure <xref linkend="F4-4" remap="4.4"/></link> illustrates a sample cp-task consisting of nine sub-tasks (nodes) V = &#x0007B;v<subscript>1</subscript>,&#x02026;,v<subscript>9</subscript>&#x0007D; and 12 precedence constraints (arcs). The number inside each node represents its WCET. Two of the nodes, v<subscript>2</subscript> and v<subscript>6</subscript>, form a conditional pair, meaning that only one sub-task between v<subscript>3</subscript> and v<subscript>4</subscript> will be executed (but never both), depending on a conditional clause. The length (longest path) of this cp-task is <emphasis>L</emphasis> = 8, and is given by the chain (v<subscript>1</subscript>, v<subscript>2</subscript>, v<subscript>4</subscript>, v<subscript>6</subscript>, v<subscript>7</subscript>, v<subscript>9</subscript>). Its volume is 14 units, while its worst-case workload must take into account that either v<subscript>3</subscript> or v<subscript>4</subscript> are executed at every task instance. Since v<subscript>4</subscript> corresponds to the branch with the largest workload, <emphasis>W</emphasis> = 11.</para>
<para>To further clarify the restrictions imposed to the graph structure, note that v<subscript>4</subscript> cannot be connected to v<subscript>5</subscript>, because this would violate the correctness of conditional constructs and the semantics of the precedence relation. In fact, if they were connected and v<subscript>3</subscript> were executed, then v<subscript>5</subscript> would wait forever, since v<subscript>4</subscript> is not executed. For the same reason, no connection is possible between v<subscript>4</subscript> and v<subscript>3</subscript>, as they belong to different branches of the same conditional statement.</para>
<para>In the following sections, we will consider the dynamic approach consisting of a best-effort mapper, coupled with a fixed priority global scheduler. RT tasks are indexed according to their priorities, being &#x003C4;<subscript>1</subscript> the highest priority one. For details on the scheduling algorithm and mapping, please refer to P-SOCRATES project&#x02019;s Deliverable D3.3.2 [19]. To understand the following analysis, it is sufficient to observe that the adopted scheduler allows a work-conserving behavior, never idling a core whenever there is some pending workload to execute.</para>
</section>
<section class="lev2" id="sec4-5-2">
<title>4.5.2 Critical Interference of cp-tasks</title>
<para>We now present a schedulability analysis for cp-tasks globally scheduled by any work-conserving scheduler. The analysis is based on the notion of <emphasis>interference</emphasis>. In the existing literature for globally scheduled sequential task systems, the interference on a task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is defined as the sum of all intervals in which <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is ready, but cannot execute because all cores are busy executing other tasks. We modify this definition to adapt it to the parallel nature of cp-tasks, by introducing the concept of critical interference.</para>
<para>Given a set of cp-tasks &#x003C4; and a work-conserving scheduler, we define the <emphasis>critical chain</emphasis> of a task as follows.</para>
<para><emphasis role="strong">Definition 4.4</emphasis>. The critical chain <emphasis>&#x003BB;<subscript>k</subscript><superscript>&#x02217;</superscript></emphasis> of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is the chain of nodes of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> that leads to its worst-case response-time R<subscript><emphasis>k</emphasis></subscript>.</para>
<para>The critical chain of cp-task &#x003C4;<subscript><emphasis role="rm">k</emphasis></subscript> is in principle determined by taking the sink vertex v<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">sink</emphasis></superscript> of the worst-case instance of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> (i.e., the job of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> that has the largest response-time in the worst-case scenario), and recursively pre-pending the last to complete among the predecessor nodes (whether conditional or not), until the source vertex v<subscript><emphasis role="rm">k,1</emphasis></subscript> has been included in the chain.</para>
<para>A critical node of task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is a node that belongs to <emphasis>&#x003C4;<subscript>k</subscript></emphasis>&#x02019;s critical chain. Since the response-time of a cp-task is given by the response-time of the sink vertex of the task, the sink node is always a critical node. For deriving the worst-case response-time of a task, it is then sufficient to characterize the maximum interference suffered by its critical chain.</para>
<para><emphasis role="strong">Definition 4.5</emphasis>. The critical interference I<subscript><emphasis>k</emphasis></subscript> on task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is defined as the cumulative time during which some critical nodes of the worst-case instance of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> are ready, but do not execute because all cores are busy.</para>
<para><emphasis role="strong">Lemma 4.1</emphasis>. Given a set of cp-tasks &#x003C4; scheduled by any work-conserving algorithm on <emphasis>m</emphasis> identical processors, the worst-case response-time of each task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq4-1.jpg"/></para>
<para><emphasis>Proof</emphasis>. Let r<subscript><emphasis role="rm">k</emphasis></subscript> be the release time of the worst-case instance of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>. In the scheduling window [<emphasis>r<subscript>k</subscript>, r<subscript>k</subscript> + R<subscript>k</subscript></emphasis>], the critical chain will require len(&#x003BB;<subscript><emphasis>k</emphasis></subscript><superscript>&#x02217;</superscript>) time-units to complete. By Definition 4.5, at any time in this window in which <emphasis>&#x003C4;<subscript>k</subscript></emphasis> does not suffer critical interference, some node of the critical chain is executing. Therefore <emphasis>R<subscript>k</subscript> - I<subscript>k</subscript> = len(&#x003BB;<subscript>k</subscript><superscript>&#x02217;</superscript>)</emphasis>.</para>
<para>The difficulty in using Lemma 4.1 for schedulability analysis is that the term I<subscript><emphasis role="rm">k</emphasis></subscript> may not be easy to compute. An established solution is to express the total interfering workload as a function of individual contributions of the interfering tasks, and then upper-bound such contributions with the worst-case workload of each interfering task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>.</para>
<para>In the following, we explain how such interfering contributions can be computed, and how they relate to each other to determine the total interfering workload.</para>
<para><emphasis role="strong">Definition 4.6</emphasis>. The critical interference I<subscript><emphasis>i,k</emphasis></subscript> imposed by task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> on task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is defined as the cumulative workload executed by sub-tasks of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> while a critical node of the worst-case instance of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is ready to execute but is not executing.</para>
<para><emphasis role="strong">Lemma 4.2</emphasis>. For any work-conserving algorithm, the following relation holds:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq4-2.jpg"/></para>
<para><emphasis>Proof</emphasis>. By the work-conserving property of the scheduling algorithm, whenever a critical node of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is interfered, all <emphasis>m</emphasis> cores are busy executing other sub-tasks. The total amount of workload executed by sub-tasks interfering with the critical chain of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is then <emphasis>mI<subscript>k</subscript></emphasis>. Hence,</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg80-1.jpg"/></para>
<para>By reordering the terms, the lemma follows.</para>
<para>Note that when <emphasis>i = k</emphasis>, the critical interference I<subscript><emphasis>k,k</emphasis></subscript> may include the interfering contributions of non-critical subtasks of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> on itself, that is, the self-interference of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>. By combining Equations (4.1) and (4.2), the response-time of a task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> can be rewritten as:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq4-3.jpg"/></para>
<para>In the following, we will show how to provide upper bounds on the unknown terms of Equation (4.3) for systems adopting a global fixed-priority scheduler with preemption support.</para>
</section>
<section class="lev2" id="sec4-5-3">
<title>4.5.3 Response Time Analysis</title>
<para>In this section, we derive an upper-bound on the worst-case response-time of each cp-task using Equation (4.3). To this aim we need to compute the interfering contributions I<subscript><emphasis>i,k</emphasis></subscript>. In the sequel, we first consider the inter-task interference (<emphasis>i&#x02260;k</emphasis>) and then the intra-task interference (<emphasis>i = k</emphasis>).</para>
<section class="lev2" id="sec4-5-3-1">
<title>4.5.3.1 Inter-task interference</title>
<para>We divide the contribution to the workload of an interfering task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> in a window of interest between carry-in, body, and carry-out jobs. The <emphasis>carry-in job</emphasis> is the first instance of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> that is part of the window of interest and has release time before and deadline within the window of interest. The <emphasis>carry-out job</emphasis> is the last instance of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> executing in the window of interest, having a deadline after the window of interest. All other instances of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> are named <emphasis>body jobs</emphasis>. For sequential task-sets, an upper-bound on the workload of an interfering task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> within a window of length L occurs when the first job of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> starts executing as late as possible (with a starting time aligned with the beginning of the window of interest) and later jobs are executed as soon as possible (see <link linkend="F4-5">Figure <xref linkend="F4-5" remap="4.5"/></link>).</para>
<fig id="F4-5" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-5">Figure <xref linkend="F4-5" remap="4.5"/></link></label>
<caption><para>Worst-case scenario to maximize the workload of an interfering task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> in the sequential case.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_5.jpg"/>
</fig>
<para>For cp-task systems, it is more difficult to determine a configuration that maximizes the carry-in and carry-out contributions. In fact:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Due to the precedence constraints and different degree of parallelism of the various execution paths of a cp-task, it may happen that a larger workload is executed within the window if the interfering task is shifted left, i.e., by decreasing the carry-in and increasing the carry-out contributions. This happens for example when the first part of the carry-in job has little parallelism, while the carry-out part at the end of the window contains multiple parallel sub-tasks.</para></listitem>
<listitem>
<para>A sustainable schedulability analysis [10] must guarantee that all tasks meet their deadlines even when some of them execute less than the worst-case. For example, one of the sub-tasks of an execution path of a cp-task may execute for less than its WCET C<subscript><emphasis role="rm">i,j</emphasis></subscript>. This may lead to larger interfering contributions within the window of interest (e.g., a parallel section of a carry-out job is included in the window due to an earlier completion of a preceding sequential section).</para></listitem>
<listitem>
<para>The carry-in and carry-out contribution of a cp-task may correspond to different conditional paths of the same task, with different levels of parallelism.</para></listitem>
</orderedlist>
<para>To circumvent the above issues, we consider a scenario in which each interfering job of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> executes for its worst-case workload W<subscript><emphasis role="rm">i</emphasis></subscript>, i.e., the maximum amount of workload that can be generated by a single instance of a cp-task. We defer the computation of W<subscript><emphasis role="rm">i</emphasis></subscript> to Section 4.5.3. The next lemma provides a safe upper-bound on the workload of a task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> within a window of interest of length L.</para>
<para><emphasis role="strong">Lemma 4.3</emphasis>. An upper-bound on the workloads of an interfering task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> in a window of</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg81-1.jpg"/></para>
<para>length L is given by</para>
<para><emphasis>Proof</emphasis>. Consider a situation in which all instances of <emphasis>i</emphasis> execute for their worst-case workload W<subscript><emphasis role="rm">i</emphasis></subscript>. The highest workload within a window of length L for such a task configuration is produced when the carry-in and carry-out contributions are evenly distributed among all cores, as shown in <link linkend="F4-6">Figure <xref linkend="F4-6" remap="4.6"/></link>. Note that distributing the carry-in or carry-out contributions on a smaller number of cores may not increase the workload within the window. Moreover, other task configurations with a smaller workload for the carry-in or carry-out instance cannot lead to a higher workload in the window of interest: although a reduced carry-in workload may allow including a larger part of the carry-out (as in shifting right the window of interest by W<subscript><emphasis>i</emphasis></subscript> = <emphasis>m</emphasis> in the figure), the carry-out part that enters the window from the right cannot be larger than the carry-in reduction.</para>
<para>An upper-bound on the number of carry-in and body instances that may execute within the window is</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg82-1.jpg"/></para>
<para>each one contributing for W<subscript><emphasis role="rm">i</emphasis></subscript>. The portion of the carry-out job included in the window of interest is (L + R<subscript><emphasis role="rm">i</emphasis></subscript> - W<subscript><emphasis role="rm">i</emphasis></subscript>/<emphasis>m</emphasis>) <emphasis>mod</emphasis> T<subscript><emphasis role="rm">i</emphasis></subscript>. Since at most <emphasis>m</emphasis> cores may be occupied by the carryout job within that interval, and the carry-out job cannot execute for more than W<subscript><emphasis role="rm">i</emphasis></subscript> units, the lemma follows.</para>
<fig id="F4-6" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-6">Figure <xref linkend="F4-6" remap="4.6"/></link></label>
<caption><para>Worst-case scenario to maximize the workload of an interfering cp-task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_6.jpg"/>
</fig>
</section>
<section class="lev2" id="sec4-5-3-2">
<title>4.5.3.2 Intra-task interference</title>
<para>We now consider the remaining terms of Equation (4.3), which take into account the contribution of the considered task to its overall response-time, and we compute an upper-bound on</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg82-2.jpg"/></para>
<para><emphasis role="strong">Lemma 4.4</emphasis>. For a constrained deadline cp-task system scheduled with any work-conserving algorithm, the following relation holds for any task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq4-4.jpg"/></para>
<para><emphasis>Proof</emphasis>. Since we are in a constrained deadline setting, a job will never be interfered with by other jobs of the same task. W<subscript><emphasis role="rm">k</emphasis></subscript> being the maximum possible workload produced by a job of cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, the portion that may interfere with the critical chain <emphasis>&#x003BB;<subscript>k</subscript></emphasis> is W<subscript><emphasis role="rm">k</emphasis></subscript>-len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>). Then, I<subscript><emphasis role="rm">k,k</emphasis></subscript> &#x02264; W<subscript><emphasis role="rm">k</emphasis></subscript>-len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>). Hence,</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq4-5.jpg"/></para>
<para>Since len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) &#x02264; L<subscript><emphasis role="rm">k</emphasis></subscript> and m &#x02265; 1, the lemma follows.</para>
<para>Since Z<subscript><emphasis role="rm">k</emphasis></subscript> includes only the contribution of task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, one may think that the sum [len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) + 1/m I<subscript><emphasis role="rm">k,k</emphasis></subscript>] is equal to the worst-case response-time of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> when it is executed in isolation on the multi-core system (i.e., the makespan of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>).</para>
<para>However, this is not true. For example, consider the case of a cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> with only one if-then-else statement; assume that when the &#x0201C;if&#x0201D; part is executed, the task executes one sub-task of length 10; otherwise, the task executes two parallel sub-tasks of length 6 each. When <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is executed in isolation on a two-core platform, the makespan is clearly given by the &#x0201C;if&#x0201D; branch, i.e., 10. When instead <emphasis>&#x003C4;<subscript>k</subscript></emphasis> can be interfered with by one job of a task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> which executes a single sub-task of length 6, the worst-case response time of <emphasis>&#x003C4;<subscript>k</subscript></emphasis> occurs when the &#x0201C;else&#x0201D; branch is executed, yielding a response time of 12. The share of the response time due to the term len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) + 1/m I<subscript><emphasis role="rm">k,k</emphasis></subscript> in Equation (4.3) is 6 + (1 = 2)6 = 9, which is strictly smaller than the makespan. Note that len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) + 1/m I<subscript><emphasis role="rm">k,k</emphasis></subscript> does not even represent a valid lower bound on the makespan. This can be seen by replacing the &#x0201C;if&#x0201D; branch in the above example with a shorter subtask of length 8, giving a makespan of 8. For this reason, one cannot replace the term len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) + 1/m I<subscript><emphasis role="rm">k,k</emphasis></subscript> in Equation (4.4) with the makespan of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>.</para>
<para>The right-hand side of Equation (4.4) (L<subscript><emphasis role="rm">k</emphasis></subscript> + 1/m(W<subscript><emphasis role="rm">k</emphasis></subscript> &#x02013; L<subscript><emphasis role="rm">k</emphasis></subscript>)) has been therefore introduced to upper-bound the term len(&#x003BB;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>&#x02217;</superscript>) + 1/m I<subscript><emphasis role="rm">k,k</emphasis></subscript>. Interestingly, this quantity does also represent a valid upper-bound on the makespan of <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, so that it can be used to bound the response time of a cp-task executing in isolation. We omit the proof that is identical to the proofs of the given bounds, considering only the interference due to the task itself.</para>
</section>
<section class="lev2" id="sec4-5-3-3">
<title>4.5.3.3 Computation of cp-task parameters</title>
<para>The upper-bounds on the interference given by Lemmas 4.3, 4.4, and 4.5 require the computation of two characteristic parameters for each cp-task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>: the worst-case workload W<subscript><emphasis role="rm">k</emphasis></subscript> and the length of the longest chain L<subscript><emphasis role="rm">k</emphasis></subscript>. The longest path of a cp-task can be computed in exactly the same way as the longest path of a classical DAG task, since any conditional branch defines a set of possible paths in the graph. For this purpose, conditional nodes can be considered as if they were simply regular nodes. The computation can be implemented time linearly in the size of the DAG by standard techniques, see e.g., Bonifaci et al. [11] and references therein.</para>
<para>The computation of the worst-case workload of a cp-task is more involved. We hereafter show an algorithm to compute W<subscript><emphasis role="rm">k</emphasis></subscript> for each task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> in time quadratic in the DAG size, whose pseudocode is shown in Algorithm 4.1.</para>
<para>The algorithm first computes a topological order of the DAG <footnote id="fn4_2" label="2"> <para>A topological order is such that if there is an arc from <emphasis>u</emphasis> to <emphasis>v</emphasis> in the DAG, then <emphasis>u</emphasis> appears before <emphasis>v</emphasis> in the topological order. A topological order can be easily computed in time linear in the size of the DAG (see any basic algorithm textbook, such as [17]).</para></footnote>. Then, exploiting the (reverse) topological order, a simple dynamic program can compute for each node the accumulated workload corresponding to the portion of the graph already examined. The algorithm must distinguish the case when the node under analysis is the head of a conditional pair or not. If this is the case, then the maximum accumulated workload among the successors is selected; otherwise, the sum of the workload contributions of all successors is computed.</para>
<table-wrap position="float" id="A4-1">
<label>Algorithm 4.1</label>
<caption><para>Worst-case Workload Computation</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/alg4-1.jpg"/>
</table-wrap>
<para>Algorithm 4.1 takes as input the graph representation of a cp-task G and outputs its worst-case workload W. In the algorithm, for any set of nodes S, its total WCET is denoted by C(S). First, at line 2, a topological sorting of the vertices is computed and stored in the permutation. Then, the permutation is scanned in reverse order, that is, from the (unique) sink to the (unique) source of the DAG. At each iteration of the for loop at line 4, a node v<subscript><emphasis role="rm">i</emphasis></subscript> is analyzed; a set variable S(v<subscript><emphasis role="rm">i</emphasis></subscript>) is used to store the set of nodes achieving the worst-case workload of the subgraph including v<subscript><emphasis role="rm">i</emphasis></subscript> and all its descendants in the DAG. Since the sink node has no successors, S(v<superscript><emphasis role="rm">sink</emphasis></superscript>) is initialized to &#x0007B;v<superscript><emphasis role="rm">sink</emphasis></superscript>&#x0007D;at line 3. Then, the function SUCC(v<subscript><emphasis role="rm">i</emphasis></subscript>) computes the set of successors of v<subscript><emphasis role="rm">i</emphasis></subscript>. If that set is not empty, function ISBEGINCOND(v<subscript><emphasis role="rm">i</emphasis></subscript>) is invoked to determine whether v<subscript><emphasis role="rm">i</emphasis></subscript> is the head node of a conditional pair. If so, the node v&#x0002A; achieving the largest value of C(S(v)), among v in SUCC(v<subscript><emphasis role="rm">i</emphasis></subscript>), is computed (line 7). The set S(v&#x0002A;) therefore achieves the maximum cumulative worst-case workload among the successors of v<subscript><emphasis role="rm">i</emphasis></subscript>, and is then used to create S(v<subscript><emphasis role="rm">i</emphasis></subscript>) together with v<subscript><emphasis role="rm">i</emphasis></subscript>. Instead, whenever v<subscript><emphasis role="rm">i</emphasis></subscript> is not the head of a conditional pair, all its successors are executed at runtime. Therefore, the workload contributions of all its successors must be merged into S(v<subscript><emphasis role="rm">i</emphasis></subscript>) (line 10) together with v<subscript><emphasis role="rm">i</emphasis></subscript>. The procedure returns the worst-case workload accumulated by the source vertex, that is C(S(v<superscript><emphasis role="rm">source</emphasis></superscript>)).</para>
<para>The complexity of the algorithm is quadratic in the size of the input DAG. Indeed, there are O(&#x0007C;E&#x0007C;) set operations performed throughout the algorithm, and some operations on a set S (namely, the ones at line 7) also require computing C(S), which has cost O(&#x0007C;V&#x0007C;). So, the time complexity is O(&#x0007C;V&#x0007C;&#x0007C;E&#x0007C;). To implement the set operations, set membership arrays are sufficient.</para>
<para>One may be tempted to simplify the procedure by avoiding the use of set operations, keeping track only of the cumulative worst-case workload at each node, and allowing a linear complexity in the DAG size. However, such an approach would lead to an overly pessimistic result. Consider a simple graph with a source node forking into multiple parallel branches which then converge on a common sink. The cumulative worst-case workload of each parallel path includes the contribution of the sink. If we simply sum such contributions to derive the cumulative worst-case workload of the source, the contribution of the sink would be counted multiple times. Set operations are therefore needed to avoid accounting multiple times each node contribution.</para>
<para>We now present refinements of Algorithm 4.1 in special sub-cases of interest.</para>
</section>
</section>
<section class="lev2" id="sec4-5-4">
<title>4.5.4 Non-conditional DAG Tasks</title>
<para>The basic sporadic DAG task model does not explicitly account for conditional branches. Therefore, all vertices of a cp-task contribute to the worst-case workload, which is then equal to the volume of the DAG task:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg86-1.jpg"/></para>
<para>In this particular case, the time complexity to derive the worst-case workload of a task (quadratic in the general case), becomes O(&#x0007C;V&#x0007C;), i.e., linear in the number of vertices.</para>
</section>
<section class="lev2" id="sec4-5-5">
<title>4.5.5 Series&#x02013;Parallel Conditional DAG Tasks</title>
<para>Some programming languages yield series&#x02013;parallel cp-tasks, that is, cp-tasks that can be obtained from a single edge by series composition and/or parallel composition. For example, the cp-task in <link linkend="F4-5">Figure <xref linkend="F4-5" remap="4.5"/></link> is series&#x02013;parallel, while the cp-tasks in Figures 4.2 and 4.6 are not. Such a structure can be detected in linear time [13]. In series&#x02013;parallel graphs, for every head <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> of a conditional or parallel branch there is a corresponding tail t<subscript><emphasis role="rm">i</emphasis></subscript>. For example, in <link linkend="F4-5">Figure <xref linkend="F4-5" remap="4.5"/></link>, the tail corresponding to parallel branch head v<subscript>2</subscript> is v<subscript>9</subscript>. Algorithm 4.1 can be specialized to series&#x02013;parallel graphs. For each vertex <emphasis>u</emphasis>, the algorithm will simply keep track of the worst-case workload of the subgraph reachable from <emphasis>u</emphasis>, as follows. For each head vertex <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> of a parallel branch, the contribution from all successors should be added to <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>&#x02019;s WCET, subtracting, however, the worst-case workload of the corresponding tail <emphasis>t</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> a number of times equal to the out-degree of <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> minus 1; for each head vertex <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> of a conditional branch, only the maximum among the successors&#x02019; worst-case workloads is added to <emphasis>s</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>&#x02019;s WCET. Finally, for all non-head vertices add the worst-case workload of their unique successor to their WCET. The complexity of this algorithm reduces then to O(&#x0007C;E&#x0007C;), i.e., it becomes linear in the size of the graph.</para>
</section>
<section class="lev2" id="sec4-5-6">
<title>4.5.6 Schedulability Condition</title>
<para>Lemmas 4.3 and 4.4 and the bounds previously computed allow for proving the following theorem.</para>
<para><emphasis role="strong">Theorem 4.1</emphasis>. Given a cp-task-set globally scheduled with global FP on m cores, an upper-bound <emphasis>R<subscript>k</subscript><superscript>ub</superscript></emphasis> on the response-time of a task &#x003C4;<subscript><emphasis role="rm">k</emphasis></subscript> can be derived by the fixed-point iteration of the following expression, starting with <emphasis>R<subscript>k</subscript><superscript>ub</superscript> = L<subscript>k</subscript></emphasis>:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg87-1.jpg"/></para>
<para>where:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg87-2.jpg"/></para>
<para>because the interference from lower priority tasks can be neglected assuming a fully preemptive scheduler.</para>
<para>The schedulability of a cp-task system can then be simply checked using Theorem 4.1 to compute an upper-bound on the response-time of each task. In the FP case, the bounds are updated in decreasing priority order, starting from the highest priority task. In this case, it is sufficient to apply Theorem 4.1 only once for each task.</para>
</section>
</section>
<section class="lev1" id="sec4-6">
<title>4.6 Specializing Analysis for Limited Pre-emption Global/Dynamic Approach</title>
<para>The response time analysis in Equation (4.3) can be easily extended [20] to incorporate the impact of the limited pre-emption strategy on DAG-based task-sets <footnote id="fn4_3" label="3"> <para>This section only considers LP with eager approach. In [28], we develop the analysis for Lazy approach as well. Interested readers are encouraged to refer to it for the complete analysis.</para></footnote>. To do so, the factor that computes the inter-task interference must be augmented to incorporate the impact of lower-priority interference. Overall, the response time upper-bound can be computed as follows:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg87-3.jpg"/></para>
<para>With LP, tasks are not only interfered with by higher-priority tasks, but also by already started lower-priority tasks whose execution has not reached a pre-emption point yet, and so cannot be suspended. In the worst-case scenario, when a high-priority task <emphasis>&#x003C4;<subscript>k</subscript></emphasis> is released, all the <emphasis>m</emphasis> processors have just started executing the <emphasis>m</emphasis> largest NPRs of <emphasis>m</emphasis> different lower priority tasks. After <emphasis>&#x003C4;<subscript>k</subscript></emphasis> started executing, it could be blocked again by at most <emphasis>m</emphasis> - 1 lower priority tasks at each pre-emption point. Therefore, for sequential task-sets, the lower priority interference is upper-bounded considering: (1) the set of the longest NPR of each lower-priority task and then (2) the sum of the <emphasis>m</emphasis> and <emphasis>m</emphasis> - 1 longest NPRs of this set, as computed in [21]. This no longer holds for DAG-based task-sets, because multiple NPRs from the same task can execute in parallel. Next, we present two methods to compute the lower-priority interference in DAG-based task-sets.</para>
<section class="lev2" id="sec4-6-1">
<title>4.6.1 Blocking Impact of the Largest NPRs (LP-max)</title>
<para>The easiest way of deriving the lower-priority interference is to account for the <emphasis>m</emphasis> and <emphasis>m</emphasis> - 1 largest NPRs among all lower-priority tasks:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg88-1.jpg"/></para>
<para>where <emphasis>&#x02211; max<subscript><emphasis>&#x003C4;<subscript>i</subscript></emphasis>&#x02208;lp(k)</subscript><superscript>m</superscript></emphasis> and <emphasis>&#x02211; max<subscript><emphasis>&#x003C4;<subscript>i</subscript></emphasis>&#x02208;lp(k)</subscript><superscript>m-1</superscript></emphasis> denote the sum of the <emphasis>m</emphasis> and <emphasis>m</emphasis> - 1 largest values among the NPRs of all tasks <emphasis><emphasis>&#x003C4;<subscript>i</subscript></emphasis>&#x1D716;</emphasis> lp(k) respectively, while <emphasis>max<subscript>1&#x02264;j&#x02264;q <subscript>i+1</subscript></subscript><superscript>m</superscript></emphasis> and <emphasis>max<subscript>1&#x02264;j&#x02264;q<subscript>i+1</subscript></subscript><superscript>m+1</superscript></emphasis> denote the <emphasis>m</emphasis> and <emphasis>m</emphasis>-1 largest NPRs of a task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>. Despite its simplicity, this strategy is pessimistic because it considers that the largest <emphasis>m</emphasis> and <emphasis>m</emphasis> - 1 NPRs can execute in parallel, regardless of the precedence constraints defined in the DAG.</para>
</section>
<section class="lev2" id="sec4-6-2">
<title>4.6.2 Blocking Impact of the Largest Parallel NPRs (LP-ILP)</title>
<para>The edges in the DAG determine the maximum level of parallelism a task may exploit on <emphasis>m</emphasis> cores, which in turn determines the amount of blocking impacting over higher-priority tasks. This information must therefore be incorporated in the analysis to better upper-bound the lower-priority interference. To do so, we propose a new analysis method that incorporates the precedence constraints among NPRs, as defined by the edges in the DAG, into the LP response-time analysis. Our analysis uses the following definitions:</para>
<para><emphasis role="strong">Definition 4.7</emphasis>: The LP worst-case workload of a task executing on c cores is the sum of the WCET of the c largest NPRs that can execute in parallel.</para>
<para><emphasis role="strong">Definition 4.8</emphasis>. The overall LP worst-case workload of a set of tasks executing on <emphasis>m</emphasis> cores is the maximum time used for executing this set in a given execution scenario, i.e. fixing the number of cores used for each task.</para>
<para>Given a task <emphasis>&#x003C4;<subscript>k</subscript></emphasis>, our analysis derives the lower-priority interference of lp(k) by computing new <emphasis>&#x00394;<subscript>k</subscript><superscript>m</superscript></emphasis> and <emphasis>&#x00394;<subscript>k</subscript><superscript>m+1</superscript></emphasis> factors in a three-step process:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Identify the LP worst-case workload of each task in lp(k) when executing on 1 to <emphasis>m</emphasis> cores;</para></listitem>
<listitem>
<para>Compute the overall LP worst-case workload of lp(k) for all possible execution scenarios;</para></listitem>
<listitem>
<para>Select the scenario that maximizes the lower-priority interference.</para></listitem>
</orderedlist>
<para>In order to facilitate the explanation of the three steps, the next sections consider an lp(k) composed of four DAG-tasks &#x0007B;&#x003C4;<subscript>1</subscript>, &#x003C4;<subscript>2</subscript>, &#x003C4;<subscript>3</subscript>, &#x003C4;<subscript>4</subscript>&#x0007D; (see <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link>), executed on an <emphasis>m</emphasis> = 4 core platform.</para>
<para>The nodes (NPRs) of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> are labeled as v<subscript><emphasis role="rm">i,j</emphasis></subscript> with their WCET (C<subscript><emphasis role="rm">i,j</emphasis></subscript>) between parenthesis.</para>
<fig id="F4-7" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link></label>
<caption><para>DAGs of lp(k) tasks; the C<subscript>i,j</subscript> of each node v<subscript>i,j</subscript> is presented in parenthesis.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_7.jpg"/>
</fig>
<section class="lev2" id="sec4-6-2-1">
<title>4.6.2.1 LP worst-case workload of a task executing on c cores</title>
<para>Given a task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, this step computes an array &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript> of size <emphasis>m</emphasis>, which includes the worst-case workload of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> when NPRs are distributed over c cores, being c = &#x0007B;1,&#x02026;,m&#x0007D; the index inside &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>. Each element &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] is computed as follows:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg90-1.jpg"/></para>
<para>where <emphasis>max<subscript>c</subscript><superscript>parallel</superscript></emphasis> is the sum of the c largest NPRs of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> that can execute in parallel, maximizing the interference when using c cores. To this aim, the sum must consider the edges of <emphasis>&#x003C4;<subscript>i</subscript></emphasis>&#x02019;s DAG to determine which NPRs can actually execute in parallel. Section 4.7.3 presents the algorithm that derives, for each NPR of <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, the set of NPRs from the same task that can potentially execute in parallel with it.</para>
<para><link linkend="T4-1">Table <xref linkend="T4-1" remap="4.1"/></link> shows the array &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript> for each of the tasks shown in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link> with <emphasis>m</emphasis> = 4. For example, the worst-case workload &#x003BC;<subscript>4</subscript> [2] occurs when NPRs v<subscript>4,3</subscript> and v<subscript>4,4</subscript> execute in parallel, with an overall impact of 9 time units. &#x003C4;<subscript>2</subscript> has a maximum parallelism of 2, so &#x003BC;<subscript>2</subscript> [3] and &#x003BC;<subscript>2</subscript> [4] are equal to 0.</para>
</section>
<section class="lev2" id="sec4-6-2-2">
<title>4.6.2.2 Overall LP worst-case workload</title>
<para>The lower-priority interference depends on how the execution of lp(k) is distributed across the <emphasis>m</emphasis> cores. We define e<superscript><emphasis role="rm">m</emphasis></superscript> = &#x0007B;s<subscript>1</subscript>,&#x02026;,s<subscript><emphasis role="rm">p(m)</emphasis></subscript>&#x0007D; as the set of different execution scenarios (and so interference scenarios) of lp(k) running on <emphasis>m</emphasis> cores. p(m) is equal to the number of partitions <footnote id="fn4_4" label="4"> <para>In number theory and combinatory, a partition of a positive integer <emphasis>m</emphasis> is a way of writing <emphasis>m</emphasis> as a sum of positive integers. Two sums that differ only in the order of their summands are considered the same partition.</para></footnote> of <emphasis>m</emphasis>, and can be computed with the pentagonal number theorem from Euler&#x02019;s formulation:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg90-2.jpg"/></para>
<para>where the sum is over all nonzero integers q (positive and negative) [22].</para>
<table-wrap position="float" id="T4-1">
<label><link linkend="T4-1">Table <xref linkend="T4-1" remap="4.1"/></link></label>
<caption><para>Worst-case workloads of tasks in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link></para></caption>
<table cellspacing="5" cellpadding="5" frame="box" rules="all">
<thead>
<tr>
<td valign="bottom" align="center">&#x003BC;<subscript>1</subscript>[c]</td>
<td valign="bottom" align="center">&#x003BC;<subscript>2</subscript>[c]</td>
<td valign="bottom" align="center">&#x003BC;<subscript>3</subscript>[c]</td>
<td valign="bottom" align="center">&#x003BC;<subscript>4</subscript>[c]</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>1,6</subscript> or <emphasis>C</emphasis><subscript>1,8</subscript> = 3</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>2,2</subscript> = 4</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>3,1</subscript> = 6</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>4,1</subscript> or <emphasis>C</emphasis><subscript>4,4</subscript> = 5</td>
</tr>
<tr>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>1,6</subscript> + <emphasis>C</emphasis><subscript>1,7</subscript> = 5</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>2,2</subscript> + <emphasis>C</emphasis><subscript>2,3</subscript> = 7</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>3,3</subscript> + <emphasis>C</emphasis><subscript>3,4</subscript> = 7</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>4,4</subscript> + <emphasis>C</emphasis><subscript>4,3</subscript> = 9</td>
</tr>
<tr>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>1,6</subscript> + <emphasis>C</emphasis><subscript>1,4</subscript> + <emphasis>C</emphasis><subscript>1,5</subscript> = 6</td>
<td valign="top" align="center">0</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>3,3</subscript> +<emphasis>C</emphasis><subscript>3,4</subscript> +<emphasis>C</emphasis><subscript>3,2</subscript> or <emphasis>C</emphasis><subscript>3,5</subscript> = 9</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>4,4</subscript> + <emphasis>C</emphasis><subscript>4,3</subscript> + <emphasis>C</emphasis><subscript>4,5</subscript> = 12</td>
</tr>
<tr>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>1,2</subscript> + <emphasis>C</emphasis><subscript>1,3</subscript> + <emphasis>C</emphasis><subscript>1,4</subscript> + <emphasis>C</emphasis><subscript>1,5</subscript> = 5</td>
<td valign="top" align="center">0</td>
<td valign="top" align="center"><emphasis>C</emphasis><subscript>3,2</subscript> + <emphasis>C</emphasis><subscript>3,3</subscript> + <emphasis>C</emphasis><subscript>3,4</subscript> + <emphasis>C</emphasis><subscript>3,5</subscript> = 11</td>
<td valign="top" align="center">0</td>
</tr>
</tbody>
</table>
</table-wrap>
<para><link linkend="T4-2">Table <xref linkend="T4-2" remap="4.2"/></link> the five possible execution scenarios assuming four cores [e<subscript>4</subscript>, p(4) = 5]. The number of tasks being executed in each execution scenario s<subscript><emphasis role="rm">l</emphasis></subscript> in <emphasis>e<superscript>m</superscript></emphasis> is given by its cardinality, i.e., &#x0007C;s<subscript><emphasis role="rm">l</emphasis></subscript>&#x0007C;.</para>
<table-wrap position="float" id="T4-2">
<label><link linkend="T4-2">Table <xref linkend="T4-2" remap="4.2"/></link></label>
<caption><para>Five possible scenarios of taskset in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link>, assuming a four core system</para></caption>
<table cellspacing="5" cellpadding="5" frame="box" rules="all">
<thead>
<tr>
<td valign="bottom" align="left">s<subscript>p</subscript> &#x02208; e<superscript>4</superscript></td>
<td valign="bottom" align="center">&#x0007C;s<subscript>p</subscript>&#x0007C;</td>
<td valign="bottom" align="left">Execution scenario description</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>1</subscript> = &#x0007B;1,1,1,1&#x0007D;</td>
<td valign="top" align="center">4</td>
<td valign="top" align="left">Each task runs in 1 core</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>2</subscript> = &#x0007B;2,2&#x0007D;</td>
<td valign="top" align="center">2</td>
<td valign="top" align="left">Each task runs in 2 cores</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>3</subscript> = &#x0007B;2,1,1&#x0007D;</td>
<td valign="top" align="center">3</td>
<td valign="top" align="left">1 task runs in 2 cores and 2 task in 1 cores each</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>4</subscript> = &#x0007B;3,1&#x0007D;</td>
<td valign="top" align="center">2</td>
<td valign="top" align="left">1 task runs in 3 cores and 1 task in 1 core</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>5</subscript> = &#x0007B;4&#x0007D;</td>
<td valign="top" align="center">1</td>
<td valign="top" align="left">1 task runs in 4 cores</td></tr>
</tbody>
</table>
</table-wrap>
<para>Each execution scenario s<subscript><emphasis role="rm">l</emphasis></subscript> in <emphasis>e<superscript>m</superscript></emphasis> has an associated overall worst-case workload, computed as:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg91-1.jpg"/></para>
<para>Where the right-hand side represents the sum of the &#x0007C;s<subscript><emphasis role="rm">l</emphasis></subscript>&#x0007C; largest combinations of &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript> that fits in the scenario s<subscript><emphasis role="rm">l</emphasis></subscript>, and so maximizes the interference. Section 4.7.3 formulates the above equation as an ILP.</para>
<para><link linkend="T4-3">Table <xref linkend="T4-3" remap="4.3"/></link> shows the &#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>] of each execution scenario and the &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] considered in <link linkend="T4-1">Table <xref linkend="T4-1" remap="4.1"/></link> and 4.2. For instance, the overall worst-case workload of s<subscript>3</subscript>, &#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s3] = 19 results when &#x003C4;<subscript>4</subscript> executes on two cores (&#x003BC;<subscript>4</subscript>[2] = 9), and &#x003C4;<subscript>2</subscript> and &#x003C4;<subscript>3</subscript> execute on one core each (&#x003BC;<subscript>2</subscript> [1] = 4 and &#x003BC;<subscript>3</subscript> [1] = 6).</para>
<table-wrap position="float" id="T4-3">
<label><link linkend="T4-3">Table <xref linkend="T4-3" remap="4.3"/></link></label>
<caption><para>Computed worst-case workload for each of the scenarios in <link linkend="T4-2">Table <xref linkend="T4-2" remap="4.2"/></link></para></caption>
<table cellspacing="5" cellpadding="1" frame="box" rules="cols">
<thead>
<tr>
<td valign="bottom" align="left">s<subscript>l</subscript></td>
<td valign="bottom" align="center">&#x003C1;<subscript>k</subscript>[s<subscript>l</subscript>]</td>
</tr>
<tr>
<td colspan="2"><emphasis role="cline"/></td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>1</subscript></td>
<td valign="top" align="center">&#x003BC;<subscript>1</subscript>[1] + &#x003BC;<subscript>2</subscript>[1] + &#x003BC;<subscript>3</subscript>[1] + &#x003BC;<subscript>4</subscript>[1] = 18</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>2</subscript></td>
<td valign="top" align="center">&#x003BC;<subscript>2</subscript>[2] or &#x003BC;<subscript>3</subscript>[2] + &#x003BC;<subscript>4</subscript>[2] = 16</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>3</subscript></td>
<td valign="top" align="center">&#x003BC;<subscript>4</subscript>[2] + &#x003BC;<subscript>2</subscript>[1] + &#x003BC;<subscript>3</subscript>[1] = 19</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>4</subscript></td>
<td valign="top" align="center">&#x003BC;<subscript>4</subscript>[3] + &#x003BC;<subscript>3</subscript>[1] = 18</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>s</emphasis><subscript>5</subscript></td>
<td valign="top" align="center">&#x003BC;<subscript>3</subscript>[4] = 11</td></tr>
</tbody>
</table>
</table-wrap>
</section>
<section class="lev2" id="sec4-6-2-3">
<title>4.6.2.3 Lower-priority interference</title>
<para>Finally, given the overall worst-case workload for each scenario &#x003BC;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>], the lower-priority interference of lp(k) can be reformulated as the maximum overall worst-case workload among all scenarios:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg91-2.jpg"/></para>
<para>where the right-hand sides provide the maximum worst-case workload among <emphasis>e<superscript>m</superscript></emphasis> and <emphasis>e<superscript>m-1</superscript></emphasis> scenarios.</para>
<para>The lower-priority interference of lp(k) is given by the maximum &#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>], i.e., &#x00394;<subscript><emphasis role="rm">k</emphasis></subscript><superscript>4</superscript> = 19. On the contrary, the pessimistic approach selects the sum of the <emphasis>m</emphasis> largest NPRs among all lower-priority tasks, i.e., &#x00394;<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">4</emphasis></superscript> = C<subscript><emphasis role="rm">3,1</emphasis></subscript>+C<subscript><emphasis role="rm">4,1</emphasis></subscript>+C<subscript><emphasis role="rm">4,4</emphasis></subscript>+C<subscript><emphasis role="rm">2,2</emphasis></subscript> = 20. The pessimism comes from the fact that nodes v<subscript><emphasis role="rm">4,1</emphasis></subscript> and v<subscript><emphasis role="rm">4,4</emphasis></subscript> cannot be executed in parallel. Similarly, &#x00394;<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">3</emphasis></superscript> = 15, while the pessimistic approach gives &#x00394;<subscript><emphasis role="rm">k</emphasis></subscript><superscript><emphasis role="rm">3</emphasis></superscript> = 16.</para>
<para>Clearly, LP-ILP allows computing a tighter lower-priority interference, at the cost of increasing the complexity of deriving it, compared to the LP-max approach.</para>
</section>
</section>
<section class="lev2" id="sec4-6-3">
<title>4.6.3 Computation of Response Time Factors of LP-ILP</title>
<para>We showed that the schedulability of a DAG-based task-set under LP-ILP can be checked in pseudo-polynomial time if, beside deadline and period, we can derive: (1) the worst-case workload generated by each lower-priority task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> (i.e., &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>), and (2) the overall worst-case workload of lower-priority tasks for each execution scenario s<subscript><emphasis role="rm">l</emphasis></subscript> in e<superscript><emphasis role="rm">m</emphasis></superscript> (i.e., &#x003C1;<subscript><emphasis role="rm">m</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>]). The former can be computed at compile-time for each task, and it is independent from the task-set; the latter requires the complete task-set knowledge, and is computed at system integration time. In this section, we present the algorithms to compute these factors.</para>
<section class="lev2" id="sec4-6-3-1">
<title>4.6.3.1 Worst-case workload of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> executing on c cores: <emphasis>&#x003BC;<subscript>i</subscript></emphasis>[c]</title>
<para>&#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] is determined by the set of c NPRs of <emphasis>&#x003C4;<subscript>i</subscript></emphasis> that can potentially execute in parallel. As a first step, we identify for each NPR the set of potential parallel NPRs; then, we compute the interference of parallel execution when different numbers of cores are used.</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Computing the set of parallel NPRs: Given the DAG G<subscript><emphasis role="rm">i</emphasis></subscript> = (V<subscript><emphasis role="rm">i</emphasis></subscript>, E<subscript><emphasis role="rm">i</emphasis></subscript>), Algorithm 4.2 computes, for each NPR v<subscript><emphasis role="rm">i,j</emphasis></subscript> in V<subscript><emphasis role="rm">i</emphasis></subscript>, the set of NPRs that can execute in parallel with it.</para></listitem>
</orderedlist>
<table-wrap position="float" id="A4-2">
<label>Algorithm 4.2</label>
<caption><para>Parallel NPRs of <emphasis>&#x003C4;<subscript>i</subscript></emphasis></para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/alg4-2.jpg"/>
</table-wrap>
<para>The algorithm takes as input the DAG of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, the topological order of G<subscript><emphasis role="rm">i</emphasis></subscript>, and, for each node v<subscript><emphasis role="rm">i,j</emphasis></subscript>, the sets:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>SIBLING(v<subscript><emphasis role="rm">i,j</emphasis></subscript>), which contains the nodes which have a common predecessor with v<subscript><emphasis role="rm">i,j</emphasis></subscript>;</para></listitem>
<listitem>
<para>SUCC(v<subscript><emphasis role="rm">i,j</emphasis></subscript>), which contains the nodes reachable from v<subscript><emphasis role="rm">i,j</emphasis></subscript>; and</para></listitem>
<listitem>
<para>PRED(v<subscript><emphasis role="rm">i,j</emphasis></subscript>), which contains the nodes from which v<subscript><emphasis role="rm">i,j</emphasis></subscript> can be reached. It outputs, for each v<subscript><emphasis role="rm">i,j</emphasis></subscript>, the set Par(v<subscript><emphasis role="rm">i,j</emphasis></subscript>), containing the nodes that can execute in parallel with it.</para></listitem>
</orderedlist>
<para>The algorithm iterates twice over all nodes in V<subscript><emphasis role="rm">i</emphasis></subscript>. The first loop (lines 2&#x02013;10) adds to Par(v<subscript><emphasis role="rm">i,j</emphasis></subscript>) (line 7) the set of sibling nodes v<subscript><emphasis role="rm">i,l</emphasis></subscript> that are not connected to v<subscript><emphasis role="rm">i,j</emphasis></subscript> by an edge (line 5), and the nodes reachable from v<subscript><emphasis role="rm">i,l</emphasis></subscript> [SUCC(v<subscript><emphasis role="rm">i,l</emphasis></subscript>)], discarding those connected to v<subscript><emphasis role="rm">i,j</emphasis></subscript> by an edge (line 6). The second loop (lines 11&#x02013;15), which traverses V<subscript><emphasis role="rm">i</emphasis></subscript> in topological order, adds to Par(v<subscript><emphasis role="rm">i,j</emphasis></subscript>) (line 14) the set of nodes Par(v<subscript><emphasis role="rm">i,l</emphasis></subscript>) computed at line 7, being v<subscript><emphasis role="rm">i,l</emphasis></subscript> a node from which v<subscript><emphasis role="rm">i,j</emphasis></subscript> can be reached [v<subscript><emphasis role="rm">i,l</emphasis></subscript> in PRED(v<subscript><emphasis role="rm">i,j</emphasis></subscript>)]. From Par(v<subscript><emphasis role="rm">i,l</emphasis></subscript>) we discard the nodes from which v<subscript><emphasis role="rm">i,j</emphasis></subscript> can be reached (line 13).</para>
<para>As an example, consider node v<subscript><emphasis role="rm">1,3</emphasis></subscript> of &#x003C4;<subscript><emphasis role="rm">1</emphasis></subscript> in <link linkend="F4-7">Figure <xref linkend="F4-7" remap="4.7"/></link>. The first loop iterates over the sibling nodes v<subscript><emphasis role="rm">1,2</emphasis></subscript>, v<subscript><emphasis role="rm">1,4</emphasis></subscript>, and v<subscript><emphasis role="rm">1,5</emphasis></subscript>. None of them is connected to v<subscript><emphasis role="rm">1,3</emphasis></subscript> by an edge (lines 4 and 5); also, SUCC(v<subscript><emphasis role="rm">1,2</emphasis></subscript>) = &#x0007B;v<subscript><emphasis role="rm">1,6</emphasis></subscript>,v<subscript><emphasis role="rm">1,8</emphasis></subscript>&#x0007D;, SUCC(v<subscript><emphasis role="rm">1,4</emphasis></subscript>) = &#x0007B;v<subscript><emphasis role="rm">1,7</emphasis></subscript>,v<subscript><emphasis role="rm">1,8</emphasis></subscript>&#x0007D;, and SUCC(v<subscript><emphasis role="rm">1,5</emphasis></subscript>) = &#x0007B;v<subscript><emphasis role="rm">1,7</emphasis></subscript>,v<subscript><emphasis role="rm">1,8</emphasis></subscript>&#x0007D;. The algorithm discards from SUCC(v<subscript><emphasis role="rm">1,2</emphasis></subscript>) nodes &#x0007B;v<subscript><emphasis role="rm">1,6</emphasis></subscript>,v<subscript><emphasis role="rm">1,8</emphasis></subscript>&#x0007D;, since they are already included in SUCC(v<subscript><emphasis role="rm">1,3</emphasis></subscript>) (line 6). This is not the case of v<subscript><emphasis role="rm">1,7</emphasis></subscript> in SUCC(v<subscript><emphasis role="rm">1,4</emphasis></subscript>) and SUCC(v<subscript><emphasis role="rm">1,5</emphasis></subscript>). Hence, we obtain Par(v<subscript><emphasis role="rm">1,3</emphasis></subscript>) = &#x0007B;v<subscript><emphasis role="rm">1,2</emphasis></subscript>,v<subscript><emphasis role="rm">1,4</emphasis></subscript>,v<subscript><emphasis role="rm">1,5</emphasis></subscript>,v<subscript><emphasis role="rm">1,7</emphasis></subscript>&#x0007D;. The second loop does not add new nodes to Par(v<subscript><emphasis role="rm">1,3</emphasis></subscript>) because the unique node from which v<subscript><emphasis role="rm">1,3</emphasis></subscript> can be reached is v<subscript><emphasis role="rm">1,1</emphasis></subscript>, and Par(v<subscript><emphasis role="rm">1,1</emphasis></subscript>) is empty. When the second loop examines node v<subscript><emphasis role="rm">1,7</emphasis></subscript>, the two sets Par(v<subscript><emphasis role="rm">1,4</emphasis></subscript>) and Par(v<subscript><emphasis role="rm">1,5</emphasis></subscript>) are considered, since v<subscript><emphasis role="rm">1,4</emphasis></subscript>,v<subscript><emphasis role="rm">1,5</emphasis></subscript> in PRED(v<subscript><emphasis role="rm">1,7</emphasis></subscript>). Then, nodes v<subscript><emphasis role="rm">1,2</emphasis></subscript>, v<subscript><emphasis role="rm">1,3</emphasis></subscript>, and v<subscript><emphasis role="rm">1,6</emphasis></subscript> are included in Par(v<subscript><emphasis role="rm">1,7</emphasis></subscript>), since none of them belongs to PRED(v<subscript><emphasis role="rm">1,7</emphasis></subscript>).</para>
<orderedlist numeration="arabic" continuation="continues" remap="2" spacing="normal">
<listitem>
<para>Impact of parallel NPRs on c cores: For any task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, we present an ILP formulation to compute &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c], i.e., the sum of the c largest NPRs in V<subscript><emphasis role="rm">i</emphasis></subscript> that, when executed in parallel, generate the worst-case workload.</para></listitem>
</orderedlist>
<para><emphasis role="strong">Parameters</emphasis>: (1) c, i.e., the maximum number of cores used by <emphasis>&#x003C4;<subscript><emphasis>i</emphasis></subscript></emphasis>; (2) v<subscript><emphasis role="rm">i,j</emphasis></subscript> in V<subscript><emphasis role="rm">i</emphasis></subscript>; (3) q<subscript><emphasis role="rm">i</emphasis>+1</subscript>, i.e., the number of NPRs; (4) C<subscript><emphasis role="rm">i,j</emphasis></subscript>; and (5) <emphasis>IsPar</emphasis><subscript><emphasis role="rm">i,j,k</emphasis></subscript> in &#x0007B;0,1&#x0007D;, i.e., a binary variable that takes 1 if v<subscript><emphasis role="rm">i,j</emphasis></subscript> and v<subscript><emphasis role="rm">i,k</emphasis></subscript> can execute in parallel, 0 otherwise.</para>
<para><emphasis role="strong">Problem variables</emphasis>: (1) b<subscript><emphasis role="rm">j</emphasis></subscript> in &#x0007B;0,1&#x0007D;, i.e., a binary variable that takes the value 1 if v<subscript><emphasis role="rm">i,j</emphasis></subscript> is one of the selected parallel NPRs, 0 otherwise, and (2) b<subscript><emphasis role="rm">j,k</emphasis></subscript> = b<subscript><emphasis role="rm">j</emphasis></subscript> <emphasis>OR</emphasis> b<subscript><emphasis role="rm">k</emphasis></subscript> with b<subscript><emphasis role="rm">j,k</emphasis></subscript> in &#x0007B;0,1&#x0007D;; j &#x02260; k, i.e., an auxiliary binary variable.</para>
<para><emphasis role="strong">Constraints</emphasis>:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg94-1.jpg"/>, i.e., only c NPRs can be selected;</para></listitem>
<listitem>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg94-2.jpg"/>, i.e., the selected NPRs can be executed in parallel; and</para></listitem>
<listitem>
<para>b<subscript><emphasis role="rm">j,k</emphasis></subscript> &#x02265; b<subscript><emphasis role="rm">j</emphasis></subscript>+ b<subscript><emphasis role="rm">k</emphasis></subscript> &#x02013; 1; b<subscript><emphasis role="rm">j,k</emphasis></subscript> &#x02264; b<subscript><emphasis role="rm">j</emphasis></subscript>; b<subscript><emphasis role="rm">j,k</emphasis></subscript> &#x02264; b<subscript><emphasis role="rm">k</emphasis></subscript>, i.e., auxiliary constraints used to model the logical <emphasis>AND</emphasis>.</para></listitem>
</orderedlist>
<para><emphasis role="strong">Objective function</emphasis>: <inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg94-3.jpg"/></para>
</section>
<section class="lev2" id="sec4-6-3-2">
<title>4.6.3.2 Overall LP worst-case workload of lp(k) per execution scenario s<subscript><emphasis role="strong">l</emphasis></subscript>: &#x003C1;<subscript><emphasis role="strong">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>]</title>
<para>Given the set lp(k) and an execution scenario s<subscript><emphasis role="rm">l</emphasis></subscript> in e<superscript><emphasis role="rm">m</emphasis></superscript>, we present an ILP formulation to derive &#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>], that is, the overall worst-case workload generated by lp(k) under s<subscript><emphasis role="rm">l</emphasis></subscript>.</para>
<para><emphasis role="strong">Parameters</emphasis>: (1) lp(k); (2) <emphasis>m</emphasis>; (3) s<subscript><emphasis role="rm">l</emphasis></subscript>; and (4) &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c], for all <emphasis>&#x003C4;<subscript>i</subscript></emphasis> in lp(k), for all c = 1,&#x02026;,m.</para>
<para><emphasis role="strong">Problem variable</emphasis>: w<subscript><emphasis role="rm">i</emphasis></subscript><superscript><emphasis role="rm">c</emphasis></superscript>, i.e., a binary variable that takes the value 1 on the selected &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] that contributes to the worst-case workload, 0 otherwise.</para>
<para><emphasis role="strong">Constraints</emphasis>:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg95-1.jpg"/>, i.e., the number of tasks contributing to the worst-case workload must be equal to the size of the execution scenario;</para></listitem>
<listitem>
<para>For all <emphasis>&#x003C4;<subscript>i</subscript></emphasis> in lp(k), <inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg95-2.jpg"/>, i.e., each task can be considered at most in one scenario;</para></listitem>
<listitem>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg95-3.jpg"/>, c in s<subscript><emphasis role="rm">l</emphasis></subscript>, i.e., for each number of cores considered in s<subscript><emphasis role="rm">l</emphasis></subscript>, there exist at least one &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] that is selected;</para></listitem>
<listitem>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg95-4.jpg"/>, the number of cores considered is m.</para></listitem>
</orderedlist>
<para><emphasis role="strong">Objective function</emphasis>: <inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg95-5.jpg"/>.</para>
</section>
</section>
<section class="lev2" id="sec4-6-4">
<title>4.6.4 Complexity</title>
<para>The complexity of the response time analysis is still pseudo-polynomial. We hereafter discuss the complexity of the LP-ILP analysis.</para>
<para>Algorithm 4.2 requires specifying for each node in V<subscript><emphasis role="rm">i</emphasis></subscript> the sets SIBLING, SUCC and PRED, which can be computed in quadratic time in the number of nodes. Similarly, the complexity of Algorithm 4.1 is quadratic in the size of the DAG task, i.e., O(&#x0007C;V<subscript><emphasis role="rm">k</emphasis></subscript>&#x0007C;<superscript><emphasis role="rm">2</emphasis></superscript>). The ILP formulation to compute &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] is performed for each task (except for the highest-priority one), and the number of cores ranges from 2 to <emphasis>m</emphasis>, hence the complexity cost is O(nm) O(ilp<subscript><emphasis role="rm">A</emphasis></subscript>). It is important to remark that Algorithm 4.2 (as well as its inputs) and the ILP that computes &#x003BC;<subscript><emphasis role="rm">i</emphasis></subscript>[c] are executed at compile-time for each task and are independent of the task-set and the system where they execute.</para>
<para>&#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>] is computed for the execution scenarios e<superscript><emphasis role="rm">m</emphasis></superscript> and e<superscript><emphasis role="rm">m-1</emphasis></superscript>, and for each task <emphasis>&#x003C4;<subscript><emphasis>k</emphasis></subscript></emphasis> (except for the lowest-priority task &#x003C4;<subscript><emphasis>n</emphasis></subscript>), hence the complexity cost is: O(n p(m)) O(ilp<subscript><emphasis role="rm">B</emphasis></subscript>) + O(n p(m-1)) O(ilp<subscript><emphasis role="rm">B</emphasis></subscript>). The cost of solving both ILP formulations is pseudo-polynomial, if the number of constraints is fixed [23]. Our ILP formulations have fixed constraints, with a function cost of O(ilp<subscript><emphasis role="rm">A</emphasis></subscript>) and O(ilp<subscript><emphasis role="rm">B</emphasis></subscript>) depending on &#x0007C;V<subscript><emphasis role="rm">k</emphasis></subscript>&#x0007C; and (m n) respectively.</para>
<para>Therefore, the cost of computing &#x003C1;<subscript><emphasis role="rm">k</emphasis></subscript>[s<subscript><emphasis role="rm">l</emphasis></subscript>] for e<superscript><emphasis role="rm">m</emphasis></superscript> dominates the cost of other operations; hence, the complexity of computing the lower priority interference is pseudo-polynomial in the number of tasks and execution scenarios, i.e., cores.</para>
</section>
</section>
<section class="lev1" id="sec4-7">
<title>4.7 Specializing Analysis for the Partitioned/Static Approach</title>
<para>The use of dynamic schedulers in certain high-criticality real-time systems may be problematic. In the automotive domain, for example, the static allocation of system components (named runnables in the AUTOSAR nomenclature) define a valid application configuration, for which the application is tested and validated. This configuration defines a specific data-flow, i.e., an order in which components process data, and an end-to-end latency between sensors and actuators, e.g., the gas pedal (sensor) and the injection (actuator). A dynamic allocation instead generates different data-flows and sensor-actuator latencies that may result in invalid configurations. The use of static allocation is therefore of paramount importance for these types of systems to guarantee the correct functionality.</para>
<para>In this section <footnote id="fn4_5" label="5"> <para>This section was published as a conference paper at AspDAC [30].</para></footnote>, a static allocation of parallel applications is proposed based on the OpenMP4 tasking model, in order to comply with the restrictive predictability requirements of safety-critical domains. An optimal task-to-thread mapping is derived based on an ILP formulation, providing the best possible response time for a given parallel task graph.</para>
<para>Two different formulations are proposed to optimally deal with both the tied and untied tasking models. Then, different heuristics are proposed for an efficient (although sub-optimal) task-to-thread mapping, with a reduced complexity. Experiments on randomly generated workloads and a real case-study are provided to characterize the worst-case response time of the proposed mapping strategies for each tasking model. The results show a significant reduction in the worst-case makespan with respect to existing dynamic mapping methods, taking a further step towards the adoption of OpenMP in real-time systems for an efficient exploitation of future embedded many-core systems.</para>
<section class="lev2" id="sec4-7-1">
<title>4.7.1 ILP Formulation</title>
<para>This section proposes an Integer Linear Programming (ILP) formulation to solve the problem of optimally allocating OpenMP tasks to threads. The problem is to determine the minimum time interval needed to execute a given OpenMP application on m threads, both in the case of tied and untied tasks. In other words, we seek to derive the optimal mapping of task (or task parts) to threads so that the task-set makespan is minimized.</para>
<para>The system model is the same as in the previous sections, with the following modifications needed to account for the OpenMP task semantics. An OpenMP application is modeled as an OpenMP-DAG G composed of N tasks &#x003C4;<subscript><emphasis role="rm">1</emphasis></subscript>,&#x02026;, &#x003C4;<subscript><emphasis role="rm">N</emphasis></subscript>. Each task <emphasis>&#x003C4;<subscript><emphasis>i</emphasis></subscript></emphasis> is composed of <emphasis>n</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> parts P<subscript><emphasis role="rm">i,1</emphasis></subscript>,&#x02026;, P<subscript><emphasis role="rm">i,ni</emphasis></subscript>. The Worst-Case Execution Time (WCET) of part P<subscript><emphasis role="rm">i,j</emphasis></subscript> of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is denoted as C<subscript><emphasis role="rm">i,j</emphasis></subscript>. The total number of threads where tasks can be executed on a multi-core platform is denoted as m.</para>
<section class="lev2" id="sec4-7-1-1">
<title>4.7.1.1 Tied tasks</title>
<para>The optimal allocation problem for tied tasks is modeled by starting from the set of tasks &#x003C4;<subscript><emphasis role="rm">1</emphasis></subscript>,&#x02026;, &#x003C4;<subscript><emphasis role="rm">N</emphasis></subscript> and by adding a sink task &#x003C4;<subscript><emphasis role="rm">N+1</emphasis></subscript> with a single task part having null WCET (i.e., C<subscript><emphasis role="rm">N+1,1</emphasis></subscript> = 0) and with incoming edges from the task parts without any successors in the original OpenMP-DAG.</para>
<para>The starting time of &#x003C4;<subscript><emphasis role="rm">N+1</emphasis></subscript> corresponds to the minimum completion time of the considered application; hence it represents our minimization objective.</para>
<para><emphasis role="strong">Input parameters:</emphasis> (1) m: number of threads available for execution; (2) N: number of tasks in the system; (3) C<subscript><emphasis role="rm">i,j</emphasis></subscript>: WCET of the j-th part of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>; (4) G = (V, E): DAG representing the structure of the OpenMP application; (5) D: relative deadline of the OpenMP-DAG; (6) succ<subscript><emphasis role="rm">i,j</emphasis></subscript>: set of immediate successors of part P<subscript><emphasis role="rm">i,j</emphasis></subscript> of <emphasis>&#x003C4;<subscript>i</subscript></emphasis>; (7) rel<subscript><emphasis role="rm">i</emphasis></subscript>: set of tasks having a relative relationship with <emphasis>&#x003C4;<subscript>i</subscript></emphasis> (either as antecedents or descendants).</para>
<para><emphasis role="strong">Problem variables:</emphasis> (1) X<subscript><emphasis role="rm">i,k</emphasis></subscript> in &#x0007B;0,1&#x0007D;: binary variable that is 1 if task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is executed by thread k, 0 otherwise; (2) Y<subscript><emphasis role="rm">i,j,k</emphasis></subscript> in &#x0007B;0,1&#x0007D;: binary variable that is 1 if the j-th part of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is executed by thread k, 0 otherwise; (3) &#x003C8;<subscript><emphasis role="rm">i,j</emphasis></subscript>: integer variable that represents the starting time of part P<subscript><emphasis role="rm">i,j</emphasis></subscript> of task <emphasis>&#x003C4;<subscript>i</subscript></emphasis> (i.e., its initial offset in the optimal schedule); (4) a<subscript><emphasis role="rm">i,j,w,z,k</emphasis></subscript>, b<subscript><emphasis role="rm">i,w,k</emphasis></subscript> in &#x0007B;0,1&#x0007D;: auxiliary binary variables.</para>
<para><emphasis role="strong">Objective function:</emphasis> The objective function aims to minimize the starting time of the dummy sink task &#x003C4;<subscript><emphasis role="rm">N+1</emphasis></subscript>: min &#x003C8;<subscript><emphasis role="rm">N+1,1</emphasis></subscript> and represents the minimum makespan. A scheduling can be declared feasible if the minimum makespan is &#x003C8;<subscript><emphasis role="rm">N+1,1</emphasis></subscript> &#x02264; D.</para>
<para><emphasis role="strong">Initial Assumptions:</emphasis> (i) The first part of the first task must begin at time <emphasis>t</emphasis> = 0: &#x003C8;<subscript><emphasis role="rm">1,1</emphasis></subscript> = 0; (ii) The first task is executed by thread 1:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg97-1.jpg"/></para>
<para><emphasis role="strong">Constraints</emphasis></para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Each task is executed by only one thread:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg98-1.jpg"/></para>
<para>This constraint enforces the tied scheduling clause, i.e., for each task <emphasis>&#x003C4;<subscript>i</subscript></emphasis>, only one binary variable <emphasis>X</emphasis><subscript><emphasis role="rm">i,k</emphasis></subscript> is set to 1 among the <emphasis>m</emphasis> variables referring to the available threads.</para></listitem>
<listitem>
<para>All parts of each task are allocated to the same thread:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg98-2.jpg"/></para>
<para>This constraint establishes the correspondence between the <emphasis>X</emphasis><subscript><emphasis role="rm">i,k</emphasis></subscript> and <emphasis>Y</emphasis> <subscript><emphasis role="rm">i,j,k</emphasis></subscript> variables.</para></listitem>
<listitem>
<para>All precedence requirements between task parts must be fulfilled:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg98-3.jpg"/></para>
<para>For each pair of task parts, if a precedence constraint connects them, then the latter cannot start until the former has completed execution. Notice that this constraint also applies to the sink task &#x003C4;<subscript><emphasis role="rm">N+1</emphasis></subscript>.</para>
</listitem>
<listitem>
<para>The execution of different task parts must be non-overlapping:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg98-4.jpg"/></para>
<para>In other terms, if two task parts are allocated to the same thread, then either one finishes before the other begins, or vice versa. This constraint can be written as:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg99-1.jpg"/></para>
<para>where M is an arbitrarily large constant. Indeed, if a<subscript><emphasis role="rm">i,j,w,z,k</emphasis></subscript> = 1, then the first inequality is always inactive, while the second one is active only if Y <subscript><emphasis role="rm">i,j,k</emphasis></subscript> = 1 and Y <subscript><emphasis role="rm">w,z,k</emphasis></subscript> = 1. Similarly, if a<subscript><emphasis role="rm">i,j,w,z,k</emphasis></subscript> = 0, then the first inequality is active only if Y <subscript><emphasis role="rm">i,j,k</emphasis></subscript> = 1 and Y <subscript><emphasis role="rm">w,z,k</emphasis></subscript> = 1, while the second one is always inactive.</para>
</listitem>
<listitem>
<para>The Task Scheduling Constraint 2 (TSC 2) as described in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link> must be satisfied:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg99-2.jpg"/></para></listitem>
</orderedlist>
<para>This constraint imposes that one task cannot be allocated to a thread where another task that is neither a descendant nor an antecedent of the considered task is suspended. This is equivalent to saying that if two tasks not related by any descendance relationship are allocated to the same thread, then one of them must have finished before the other one begins. Therefore, the last task part of either task plus its WCET must be smaller than or equal to the starting time of the first task part of the other one. As for constraint (iv), it can be rewritten as:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg99-3.jpg"/></para>
<para>Note that all constraints [except constraint (iii)] need not be applied to &#x003C4;<subscript><emphasis role="rm">N+1</emphasis></subscript>.</para>
</section>
<section class="lev2" id="sec4-7-1-2">
<title>4.7.1.2 Untied tasks</title>
<para>The ILP formulation proposed for tied tasks can be applied for untied tasks with the following modifications. The initial assumption (ii) is replaced as follows: <emphasis>Y</emphasis><subscript><emphasis role="rm">1,1,1</emphasis></subscript> = 1.</para>
<para>Since different parts of the same task are allowed to be executed by different threads, constraints (i) and (ii) are replaced by:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg100-1.jpg"/></para>
<para>and the variables <emphasis>X</emphasis><subscript><emphasis role="rm">i,k</emphasis></subscript> are no longer needed. Finally, constraint (v) does not apply for untied tasks and thus the auxiliary variables <emphasis>b</emphasis><subscript><emphasis role="rm">i,w,k</emphasis></subscript> are not needed.</para>
</section>
<section class="lev2" id="sec4-7-1-3">
<title>4.7.1.3 Complexity</title>
<para>The problem of determining the optimal allocation strategy of an OpenMP-DAG composed of untied tasks has a direct correspondence with the makespan minimization problem of a set of precedence-constrained jobs (task parts in our case) on identical processors (threads in a team in our case). This problem, also known as job-shop scheduling, has been proven to be strongly NP-hard by a result of Lenstra and Rinnooy Kan [18]. The complexity of the problem for the tied tasks cannot be smaller than in the untied case. Indeed, when each task has a single task part, the problem for tied tasks reduces to that for untied tasks.</para>
<para>In the presented ILP formulations for both the tied and untied tasks, the number of variables and the number of constraints grow as <emphasis>O</emphasis>(N<superscript><emphasis role="rm">2</emphasis></superscript><emphasis>p</emphasis><superscript><emphasis role="rm">2</emphasis></superscript><emphasis>m</emphasis>), where <emphasis>p</emphasis> = max<subscript>i=1,&#x02026;,<emphasis>N</emphasis></subscript> <emphasis>n</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>.</para>
<para>Given the problem complexity and poor scalability of the ILP formulation, the next section proposes an efficient heuristic for providing sub-optimal solutions within a reasonable amount of time.</para>
</section>
</section>
<section class="lev2" id="sec4-7-2">
<title>4.7.2 Heuristic Approaches</title>
<para>In the context of production scheduling, several heuristic strategies have been proposed to solve the makespan minimization problem of precedence constrained jobs on parallel machines [20, 24]. More specifically, different priority rules have been proposed in the literature to sort a collection of jobs subject to arbitrary precedence constraints on parallel machines. Such ordering rules allow selecting the next job to be executed in the set of ready jobs.</para>
<para>The ordering rules that have been shown to perform well in the context of parallel machine scheduling are [20, 24]:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis role="strong">Longest Processing Time (LPT)</emphasis>: The job with the longest WCET is selected;</para></listitem>
<listitem>
<para><emphasis role="strong">Shortest Processing Time (SPT)</emphasis>: The job with the shortest WCET is selected;</para></listitem>
<listitem>
<para><emphasis role="strong">Largest Number of Successors in the Next Level (LNSNL)</emphasis>: The job with the largest number of immediate successors is selected;</para></listitem>
<listitem>
<para><emphasis role="strong">Largest Number of Successors (LNS)</emphasis>: The job with the largest number of successors overall is selected;</para></listitem>
<listitem>
<para><emphasis role="strong">Largest Remaining Workload (LRW)</emphasis>: The job with the largest workload to be executed by its successors is selected.</para></listitem>
</orderedlist>
<para>We build upon such results to make them applicable to the considered problem. At any time instant, the set of ready jobs of a given instance of an OpenMP-DAG corresponds to the set of task parts that have not completed execution and whose precedence constraints are fulfilled.</para>
<para>This section presents an algorithm for allocating tied and untied task parts on the different threads following one of the above-mentioned ordering criteria, such that the partial ordering between task parts is respected.</para>
<section class="lev2" id="sec4-7-2-1">
<title>4.7.2.1 Tied tasks</title>
<para>Algorithm 4.3 instantiates the procedure for the case of tied tasks, for which existing heuristic strategies cannot be directly applied. The algorithm takes the structure G of an OpenMP-DAG and the number of available threads <emphasis>m</emphasis> as inputs, and it outputs a heuristic allocation of tied OpenMP tasks to threads.</para>
<table-wrap position="float" id="A4-3">
<label>Algorithm 4.3</label>
<caption><para>Heuristic allocation of an OpenMP application comprising tied tasks</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/alg4-3.jpg"/>
</table-wrap>
<para>The idea behind the algorithm is to allocate ready task parts to the first available thread, following a pre-determined criterion to choose among ready tasks, while enforcing the specific semantics of the OpenMP tasking model. First, a list <emphasis>R</emphasis> of ready task parts is initialized with <emphasis>P</emphasis><subscript><emphasis role="rm">1,1</emphasis></subscript>, and an array <emphasis>L</emphasis> of size <emphasis>m</emphasis> with null initial values is used to store the last idle time on each thread (lines 2&#x02013;3). The while loop at lines 4&#x02013;25 iterates until all task parts have been allocated, i.e., until the size of list A, which contains the allocated jobs, reaches the total number of parts in the task-set. At each iteration, a new task part is allocated to one of the threads. Specifically, at line 5, the index <emphasis>k</emphasis> of the earliest available thread is determined by function FirstIdleThread. Then, the procedure NextReadyJob returns the ready task part <emphasis>P</emphasis><subscript><emphasis role="rm">i,j</emphasis></subscript> selected according to one of the ordering rules described above. The allocation of the selected task part must always respect TSC 2. Hence, any time the first part of a new task is selected, the function must check its descendance relationships with the tasks currently suspended on thread <emphasis>k</emphasis>, stored in the list <emphasis>S</emphasis><subscript><emphasis role="rm">k</emphasis></subscript>. If <emphasis>P</emphasis><subscript><emphasis role="rm">i,j</emphasis></subscript> is the first part of <emphasis>&#x003C4;<subscript><emphasis>i</emphasis></subscript></emphasis> (line 7), then it is allocated on core <emphasis>k</emphasis>; otherwise, it is allocated on thread &#x1D703;<subscript><emphasis role="rm">i</emphasis></subscript>, according to the tied scheduling clause. Also, if that task part is not the final one (line 9), <emphasis>&#x003C4;<subscript>i</subscript></emphasis> is appended to the list of tasks currently suspended on thread <emphasis>k</emphasis>. Otherwise, if P<subscript><emphasis role="rm">i,j</emphasis></subscript> is the final part of <emphasis>&#x003C4;<subscript><emphasis>i</emphasis></subscript></emphasis> (line 12), <emphasis>&#x003C4;<subscript><emphasis>i</emphasis></subscript></emphasis> can be removed from the list of tasks currently suspended on thread <emphasis>k</emphasis>. In both cases, the starting time of P<subscript><emphasis role="rm">i,j</emphasis></subscript> is updated, as well as the last idle time on thread <emphasis>k</emphasis> (line 15). In addition, P<subscript><emphasis role="rm">i,j</emphasis></subscript> is added to the list of allocated jobs and removed from the list of ready jobs (line 16). Once P<subscript><emphasis role="rm">i,j</emphasis></subscript> has been allocated, other jobs may become ready. All the successors of P<subscript><emphasis role="rm">i,j</emphasis></subscript> are scanned and an internal counter (F<subscript><emphasis role="rm">k,z</emphasis></subscript>) is incremented for each vertex (for loop at lines 17&#x02013;24). Once the counter reaches the number of its immediate predecessors, the task part may be appended to the list of ready vertices (line 21). Finally, the makespan corresponding to the generated allocation is returned. At the end of the algorithm, &#x003C8;<subscript><emphasis role="rm">i,j</emphasis></subscript> stores the starting time of any part P<subscript><emphasis role="rm">i,j</emphasis></subscript>in the final schedule, and &#x1D703; stores the mapping of tasks to threads.</para>
<para>The algorithm runs in polynomial time in the size of the task-set; specifically, the time complexity is <inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg103-1.jpg"/>.</para>
</section>
<section class="lev2" id="sec4-7-2-2">
<title>4.7.2.2 Untied tasks</title>
<para>Algorithm 4.3 can be applied also in the case of untied tasks with some simplifications. In particular, the function NextReadyJob does not need to check the validity of TSC 2. Hence, the array S is not required, and all the operations on S at lines 7&#x02013;14 do not need to be performed. On the other hand, the algorithm must keep track of the thread associated to each task part (instead of each task).</para>
</section>
</section>
<section class="lev2" id="sec4-7-3">
<title>4.7.3 Integrating Interference from Additional RT Tasks</title>
<para>We now generalize the static setting by considering a set of <emphasis>n</emphasis> OpenMP applications modeled as a collection of OpenMP DAGs &#x00393; = &#x0007B;G<subscript><emphasis role="rm">1</emphasis></subscript>,&#x02026;,G<subscript><emphasis role="rm">n</emphasis></subscript>&#x0007D;. Each DAG is released sporadically (or periodically) and has a relative deadline D<subscript><emphasis role="rm">i</emphasis></subscript>, which is constrained to be smaller than or equal to its corresponding period (or inter-arrival time) <emphasis>T</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>.</para>
<para>We assume that parts of each tasks are statically partitioned to the <emphasis>m</emphasis> available threads. At any time instant, the scheduler selects among the ready task parts the one that should be executed by a given thread according to partitioned fixed-priority preemptive scheduling. In addition, we assume that OpenMP applications are statically prioritized, i.e., each DAG G<subscript><emphasis role="rm">i</emphasis></subscript> is associated with a unique (fixed) priority that is used by the scheduler to select which task parts should be executed at any time instant by any of the threads.</para>
<para>In order to compute an upper-bound on the response time <emphasis>R</emphasis><subscript><emphasis role="rm">i</emphasis></subscript> of a given OpenMP-DAG <emphasis>G</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>, we proceed by computing an upper-bound on the response time of each task part in the OpenMP-DAG, following a predefined order dictated by any topological sorting of the DAG. At each step, the response time of the considered vertex is computed considering all its immediate predecessors, one at a time. A safe upper-bound on the response time of the vertex under analysis will be selected as the maximum of such values. The maximum response time among vertices without successors will be selected as upper-bound to the response time of the DAG-task <emphasis>G</emphasis><subscript><emphasis role="rm">i</emphasis></subscript>.</para>
</section>
<section class="lev2" id="sec4-7-4">
<title>4.7.4 Critical Instant</title>
<para>We hereafter prove that the synchronous periodic arrival pattern does not represent the worst-case release sequence for the OpenMP-DAG task model assumed. Consider a task-set composed of two OpenMP-DAG tasks G<subscript>1</subscript> and G<subscript>2</subscript>, whose structure and parameters are illustrated in <link linkend="F4-8">Figure <xref linkend="F4-8" remap="4.8"/></link>. The figure also reports the static allocation of task parts to threads: parts P<subscript><emphasis role="rm">1,1</emphasis></subscript>, P<subscript><emphasis role="rm">1,2</emphasis></subscript>, P<subscript><emphasis role="rm">1,4,</emphasis></subscript> and P<subscript><emphasis role="rm">2,1</emphasis></subscript> are allocated to thread m<subscript><emphasis role="rm">1</emphasis></subscript>, while part P<subscript><emphasis role="rm">1,3</emphasis></subscript> is allocated to thread m<subscript><emphasis role="rm">2</emphasis></subscript>.</para>
<fig id="F4-8" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-8">Figure <xref linkend="F4-8" remap="4.8"/></link></label>
<caption><para>Tasks example.</para></caption>
<graphic xlink:href="graphics/ch04_Fig4_8.jpg"/>
</fig>
<para>We can immediately see that R<subscript>1</subscript> = 7, as G<subscript>1</subscript> is the highest priority RT task in the system. In order to compute the response time of G<subscript>2</subscript>, we focus on thread m<subscript><emphasis role="rm">1</emphasis></subscript> and first consider the synchronous periodic arrival pattern for G<subscript><emphasis role="rm">1</emphasis></subscript>, which produces the schedule in <link linkend="F4-9">Figure <xref linkend="F4-9" remap="4.9"/></link>a and yields a response time of 21 time units for G<subscript>2</subscript>. However, if we consider the release pattern in <link linkend="F4-9">Figure <xref linkend="F4-9" remap="4.9"/></link>b, where the release of G<subscript><emphasis role="rm">1</emphasis></subscript> has an offset of two time units, we observe that the response time of G<subscript><emphasis role="rm">2</emphasis></subscript> becomes equal to 23.</para>
<fig id="F4-9" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F4-9">Figure <xref linkend="F4-9" remap="4.9"/></link></label>
<caption><para>Different release patterns for the example of <link linkend="F4-8">Figure <xref linkend="F4-8" remap="4.8"/></link>. (a) represents the most optimistic case, while (c) the most pessimistic, i.e., yelding to the highest WCET. (b) represents an intermediate case.</para></caption>
<graphic xlink:href="graphics/ch04_fig4_9.jpg"/>
</fig>
<para>This example shows that it is very difficult to exactly quantify the interference a task may suffer from higher-priority tasks in the worst-case. This is mainly due to the precedence constraints between parts of the same tasks, and to the fact that any vertex is allowed to execute on its corresponding thread only when all its predecessors (possibly allocated to different threads) have completed their execution. In order to overcome these problems, we derive a safe upper-bound on the response time of a given task by considering the densest possible packing of jobs generated by a legal schedule in any time interval. Specifically, we consider a pessimistic scenario (see <link linkend="F4-9">Figure <xref linkend="F4-9" remap="4.9"/></link>c):</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>the first instance of a higher-priority task is released as late as possible;</para></listitem>
<listitem>
<para>subsequent instances are released as soon as possible;</para></listitem>
<listitem>
<para>higher-priority jobs are considered as if precedence constraints were removed (their WCET is &#x0201C;compacted&#x0201D;).</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec4-7-5">
<title>4.7.5 Response-time Upper Bound</title>
<para>Algorithm 4.4 computes an upper-bound on the response time of an OpenMP-DAG by considering the above-described pessimistic scenario leading to the densest possible packing of higher-priority task parts:</para>
<table-wrap position="float" id="A4-4">
<label>Algorithm 4.4</label>
<caption><para>Upper-bound on the response time of an OpenMP-DAG by considering the densest possible packing of higher-priority task parts</para></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/alg4-4.jpg"/>
</table-wrap>
<para>The function SELFINTERFERENCE calculates the self-interference suffered by task part P<subscript><emphasis role="rm">k,i</emphasis></subscript> as the sum of the WCETs of all parts P<subscript><emphasis role="rm">k,j</emphasis></subscript> belonging to the same task and such that:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>they are allocated to the same thread as P<subscript><emphasis role="rm">k,i</emphasis></subscript>;</para></listitem>
<listitem>
<para>there is no path starting at P<subscript><emphasis role="rm">k,i</emphasis></subscript> that can reach P<subscript><emphasis role="rm">k,j</emphasis></subscript> ;</para></listitem>
<listitem>
<para>there is no path starting at P<subscript><emphasis role="rm">k,j</emphasis></subscript> that can reach P<subscript><emphasis role="rm">k,i</emphasis></subscript>.</para></listitem>
</orderedlist>
<para>With the above algorithm in place, different heuristics can be proposed to find a feasible allocation of task parts to threads/cores. Among the ones we tried, we found that the best schedulability performances are obtained with a Best Fit approach that works as follows:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>It assigns RT tasks in non-increasing priority order, i.e., starting from the highest priority task and moving towards lower priority ones.</para></listitem>
<listitem>
<para>For each task it defines a topological order for all task parts.</para></listitem>
<listitem>
<para>Following the topological order, each task part is assigned to the core that minimizes its partial response time, i.e., the response time of the RT task until the considered task part.</para></listitem>
<listitem>
<para>If any of the considered task parts has a partial response time that exceeds its relative deadline, the algorithm fails, declaring the RT task-set not schedulable.</para></listitem>
</itemizedlist>
<para>The partial response time of each task part can be easily computed using Algorithm 4.4, executing the operations within the for loop at line 3. Once the selection is made for a task part, there is no need to recheck the schedulability of the parts already assigned belonging to higher priority tasks, since this last assignment does not interfere with them. However, it is necessary to reconsider the task parts belonging to the same RT task that may experience an increase in the interference. The only task parts that may be affected by the last task part assigned are those that have no precedence constraints with it. For these ones, we re-compute their partial response-time after the new assignment. Since there is no backtracking in this case, the complexity of the heuristic remains reasonable, at the penalty of some added pessimism.</para>
</section>
</section>
<section class="lev1" id="sec4-8">
<title>4.8 Scheduling for I/O Cores</title>
<para>This paragraph briefly describes the scheduler adopted at host level, i.e., in the I/O cores.</para>
<para>According to system requirements, the OS running on the host processor must be Linux. Moreover, the Linux kernel must be patched with the PREEMPT_RT patch <footnote id="fn4_6" label="6"> <para>PREEMPT_RT Linux patch, <ulink url="https://rt.wiki.kernel.org">https://rt.wiki.kernel.org</ulink></para></footnote>. This is an on-going project supported by the OSADL association <footnote id="fn4_7" label="7"> <para>OSADL, Open Source Automation Development Lab, <ulink url="http://www.osadl.org/">http://www.osadl.org/</ulink></para></footnote> to add real-time performance to the Linux kernel by reducing the <emphasis>maximum</emphasis> latency experienced by an application, mainly through preemptible spinlocks and in-thread interrupt management (See also essential work in [25&#x02013;38]). The patch makes the system more predictable and deterministic; however, it often increases the <emphasis>average</emphasis> latency. Currently, the patch only partially works on the reference platform due to missing support for SMP in the Linux kernel; full support will be added during the next months. Concerning the scheduling policy, the OS must provide a fixed-priority preemptive FIFO-scheduling algorithm. Therefore, the basic scheduling algorithm will be the SCHED_FIFO policy specified by the POSIX standard. The optional requirement R5.21 suggests to have a Linux kernel higher than 3.14 for investigating potential benefits given by the dynamic-priority SCHED_DEADLINE Linux scheduler. This possibility will be explored at a later stage of the project. Access to shared resources in the host cores is handled through the Priority Inheritance (PI) policy provided by the Linux kernel.</para>
</section>
<section class="lev1" id="sec4-9">
<title>4.9 Summary</title>
<para>In this chapter, we described the design choices related to the implementation of a partitioned scheduler for allocating the computing resources to the different threads in the system. In particular, we detailed the thread model adopted in the project, and the local scheduler adopted at core level, based on fixed thread priorities.</para>
<para>Such a scheduler has then been enhanced with the enforcement of a limited pre-emption scheduling policy that corresponds to the execution model supported by the OpenMP tasking model, as well as allowing increasing the predictability of the analysis, without sacrificing the schedulability. According to the limited pre-emption scheduling model, each thread can be pre-empted only at particular pre-emption points. The framework provides a method to compute the length of the largest non-preemptive region that can be tolerated by each thread (at each different priority). Then, threads execute along non preemptive regions. In a generic model such as the one introduced in this chapter, this means inserting the minimum possible number of preemption points such that the schedulability of higher priority thread is not affected. Of course, specifying this model so that it adheres to OpenMP semantics means that the identification of these preemption points exploits information inherited from the OpenMP task semantics, i.e., OpenMP TSPs will be used as potential candidates.</para>
<para>We then described the implementation of an enhanced global scheduler with migration support. Such a scheduler is integrated with the OpenMP dynamic mapping policy to allow for a work-conserving resource allocation of computing resources. The scheduler adopts a cluster-wide ready queue where threads are ordered according to their priorities. Preemptions are allowed only at task-part boundaries when a TSP is reached. TSPs are also natural polling points to deal with new incoming offoads without requiring interrupts.</para>
<para>The task model adopted, namely the cp-task model, generalizes the classic sporadic DAG task model by integrating conditional branches. The topological structure of a cp-task graph has been formally characterized by specifying which connections are allowed between conditional and non-conditional nodes. Then, a schedulability analysis has been derived to compute a safe upper-bound on the response-time of each task in pseudo-polynomial time. Besides its reduced complexity, the proposed analysis has the advantage of requiring only two parameters to characterize the complex structure of the conditional graph of each task: the worst-case workload and the length of the longest path. Algorithms have also been proposed to derive these parameters from the DAG structure in polynomial time. Simulation experiments carried out with randomly generated cp-task workloads and real test-cases clearly showed that the proposed approach is able to improve over previously proposed solutions for tightening the schedulability analysis of sporadic DAG task systems. The first formulation of the analysis considered a full-preemption model (see [18]). Then, it has been extended to limited preemptive scheduling [24], and, finally, it has been specialized also for non-conditional DAGs [20, 29].</para>
<para>In this chapter, two methods have been proposed to compute the lower-priority interference: (1) a pessimistic but easy-to-compute method, named LP-max, which upper bounds the interference by selecting the NPRs with the longest worst-case execution time; and (2) a tighter but computationally-intensive method, named LP-ILP, which also takes into account precedence constraints among DAGs nodes in the analysis. Our results demonstrate that LP-ILP increases the accuracy of the schedulability test with respect to LP-max when considering DAG-based task-sets with different levels of parallelism.</para>
<para>The chapter then proposed an ILP formulation to derive an optimal static allocation compliant with the OpenMP4 tied and untied tasking model. With the objective of reducing the complexity of the ILP solver, five heuristics have been proposed for an efficient (although sub-optimal) allocation. Results obtained on both randomly generated task-sets and the 3DPP application (from the avionics domain) show a significant reduction in the worst-case makespan with respect to an existing schedulability upper-bound for untied tasks. Moreover, the proposed heuristics perform very well, closely matching the optimal solutions for small task-set, and outperforming the best feasible solution found by our ILP (after running the solver for a certain amount of time) for large task-sets and the 3DPP.</para>
</section>
<section class="lev1" id="sec4-10">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Liu, C., Layland, J., Scheduling algorithms for multiprogramming in a hard real-time environment. <emphasis>J. ACM</emphasis> 20, 46&#x02013;61, 1973. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Liu%2C+C%2E%2C+Layland%2C+J%2E%2C+Scheduling+algorithms+for+multiprogramming+in+a+hard+real-time+environment%2E+J%2E+ACM+20%2C+46-61%2C+1973%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Leung, J. Y. T., Whitehead, J., On the complexity of fixed-priority scheduling of periodic, real-time tasks. <emphasis>Perform. Eval.</emphasis> 2, 237&#x02013;250, 1982. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Leung%2C+J%2E+Y%2E+T%2E%2C+Whitehead%2C+J%2E%2C+On+the+complexity+of+fixed-priority+scheduling+of+periodic%2C+real-time+tasks%2E+Perform%2E+Eval%2E+2%2C+237-250%2C+1982%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Buttazzo, G., Bertogna, M., Yao, G., &#x0201C;Limited preemptive scheduling for real-time systems: a survey.&#x0201D; <emphasis>IEEE Transactions on Industrial Informatics</emphasis>, 9, 3&#x02013;15, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Buttazzo%2C+G%2E%2C+Bertogna%2C+M%2E%2C+Yao%2C+G%2E%2C+%22Limited+preemptive+scheduling+for+real-time+systems%3A+a+survey%2E%22+IEEE+Transactions+on+Industrial+Informatics%2C+9%2C+3-15%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Lehoczky, J., Sha, L., Ding, Y., &#x0201C;The rate monotonic scheduling algorithm: Exact characterization and average case behavior.&#x0201D; In <emphasis>Proceedings of the Real-Time Systems Symposium&#x02014;1989, pp. 166&#x02013;171. IEEE Computer Society Press</emphasis>, Santa Monica, California, USA, 1989. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lehoczky%2C+J%2E%2C+Sha%2C+L%2E%2C+Ding%2C+Y%2E%2C+%22The+rate+monotonic+scheduling+algorithm%3A+Exact+characterization+and+average+case+behavior%2E%22+In+Proceedings+of+the+Real-Time+Systems+Symposium-1989%2C+pp%2E+166-171%2E+IEEE+Computer+Society+Press%2C+Santa+Monica%2C+California%2C+USA%2C+1989%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Dhall, S. K., and Liu, C. L. On a real-time scheduling problem. <emphasis>Operat. Res.</emphasis>. 26, 127&#x02013;140, 1978. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Dhall%2C+S%2E+K%2E%2C+and+Liu%2C+C%2E+L%2E+On+a+real-time+scheduling+problem%2E+Operat%2E+Res%2E%2E+26%2C+127-140%2C+1978%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Baruah, S., Cohen, N., Plaxton, G., and Varvel, D., Proportionate progress: A notion of fairness in resource allocation. <emphasis>Algorithmica</emphasis> 15, 600&#x02013;625, 1996. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Baruah%2C+S%2E%2C+Cohen%2C+N%2E%2C+Plaxton%2C+G%2E%2C+and+Varvel%2C+D%2E%2C+Proportionate+progress%3A+A+notion+of+fairness+in+resource+allocation%2E+Algorithmica+15%2C+600-625%2C+1996%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Anderson, A., and Srinivasan, A., &#x0201C;Pfair scheduling: Beyond periodic task systems.&#x0201D; In <emphasis>Proceedings of the International Conference on Real-Time Computing Systems and Applications (Cheju Island, South Korea)</emphasis>, IEEE Computer Society Press, 2000. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Anderson%2C+A%2E%2C+and+Srinivasan%2C+A%2E%2C+%22Pfair+scheduling%3A+Beyond+periodic+task+systems%2E%22+In+Proceedings+of+the+International+Conference+on+Real-Time+Computing+Systems+and+Applications+%28Cheju+Island%2C+South+Korea%29%2C+IEEE+Computer+Society+Press%2C+2000%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Zhu, D., Mosse, D., and Melhem, R. G., &#x0201C;Multiple-resource periodic scheduling problem: how much fairness is necessary?&#x0201D; <emphasis>24th IEEE Real-Time Systems Symposium (RTSS)</emphasis> (Cancun, Mexico), 2003. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Zhu%2C+D%2E%2C+Mosse%2C+D%2E%2C+and+Melhem%2C+R%2E+G%2E%2C+%22Multiple-resource+periodic+scheduling+problem%3A+how+much+fairness+is+necessary%B4%22+24th+IEEE+Real-Time+Systems+Symposium+%28RTSS%29+%28Cancun%2C+Mexico%29%2C+2003%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Cho, H., Ravindran, B., and Jensen, E. D., &#x0201C;An optimal real-time scheduling algorithm for multiprocessors,&#x0201D; <emphasis>27th IEEE Real-Time Systems Symposium (RTSS)</emphasis> (Rio de Janeiro, Brazil), 2006. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Cho%2C+H%2E%2C+Ravindran%2C+B%2E%2C+and+Jensen%2C+E%2E+D%2E%2C+%22An+optimal+real-time+scheduling+algorithm+for+multiprocessors%2C%22+27th+IEEE+Real-Time+Systems+Symposium+%28RTSS%29+%28Rio+de+Janeiro%2C+Brazil%29%2C+2006%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Andersson, B., and Tovar, E., &#x0201C;Multiprocessor scheduling with few preemptions.&#x0201D; In <emphasis>Proceedings of the International Conference on Real-Time Computing Systems and Applications. (RTCSA)</emphasis>, 2006. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Andersson%2C+B%2E%2C+and+Tovar%2C+E%2E%2C+%22Multiprocessor+scheduling+with+few+preemptions%2E%22+In+Proceedings+of+the+International+Conference+on+Real-Time+Computing+Systems+and+Applications%2E+%28RTCSA%29%2C+2006%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Funaoka, K., Kato, S., and Yamasaki, N., &#x0201C;Work-conserving optimal real-time scheduling on multiprocessors.&#x0201D; In <emphasis>Proceedings of the Euromicro Conference on Real-Time Systems</emphasis>, 13&#x02013;22, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Funaoka%2C+K%2E%2C+Kato%2C+S%2E%2C+and+Yamasaki%2C+N%2E%2C+%22Work-conserving+optimal+real-time+scheduling+on+multiprocessors%2E%22+In+Proceedings+of+the+Euromicro+Conference+on+Real-Time+Systems%2C+13-22%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Funk, S., and Nadadur, V., &#x0201C;LRE-TL: An optimal multiprocessor algorithm for sporadic task sets.&#x0201D; In <emphasis>Proceedings of the Real-Time Networks and Systems Conference</emphasis>, 159&#x02013;168, 2009. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Funk%2C+S%2E%2C+and+Nadadur%2C+V%2E%2C+%22LRE-TL%3A+An+optimal+multiprocessor+algorithm+for+sporadic+task+sets%2E%22+In+Proceedings+of+the+Real-Time+Networks+and+Systems+Conference%2C+159-168%2C+2009%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Levin, G, Funk, S., Sadowski, C., Pye, I., Brandt, S, &#x0201C;DP-Fair: A Simple Model for Understanding Multiprocessor Scheduling.&#x0201D; In <emphasis>Proceedings of the 22nd Euromicro Conference on Real-Time Systems (ECRTS)</emphasis>, Brussels, Belgium, pp. 1&#x02013;10, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Levin%2C+G%2C+Funk%2C+S%2E%2C+Sadowski%2C+C%2E%2C+Pye%2C+I%2E%2C+Brandt%2C+S%2C+%22DP-Fair%3A+A+Simple+Model+for+Understanding+Multiprocessor+Scheduling%2E%22+In+Proceedings+of+the+22nd+Euromicro+Conference+on+Real-Time+Systems+%28ECRTS%29%2C+Brussels%2C+Belgium%2C+pp%2E+1-10%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Funk, S., Nelis, V., Goossens, J., Milojevic, D., Nelissen, G., and Nadadur, V., On the design of an optimal multiprocessor real-time scheduling algorithm under practical considerations (extended version). arXiv preprint arXiv:1001.4115, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Funk%2C+S%2E%2C+Nelis%2C+V%2E%2C+Goossens%2C+J%2E%2C+Milojevic%2C+D%2E%2C+Nelissen%2C+G%2E%2C+and+Nadadur%2C+V%2E%2C+On+the+design+of+an+optimal+multiprocessor+real-time+scheduling+algorithm+under+practical+considerations+%28extended+version%29%2E+arXiv+preprint+arXiv%3A1001%2E4115%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Nelissen, G., Su, H., Guo, Y., Zhu, D., Nelis, V., and Goossens, J., An optimal boundary fair scheduling. <emphasis>Real-Time Sys. J.</emphasis> 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Nelissen%2C+G%2E%2C+Su%2C+H%2E%2C+Guo%2C+Y%2E%2C+Zhu%2C+D%2E%2C+Nelis%2C+V%2E%2C+and+Goossens%2C+J%2E%2C+An+optimal+boundary+fair+scheduling%2E+Real-Time+Sys%2E+J%2E+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Regnier, P., Lima, G., Massa, E., Levin, G., and Brandt, S., &#x0201C;RUN: Optimal multiprocessor real-time scheduling via reduction to uniprocessor,&#x0201D; <emphasis>IEEE 32nd Real-Time Systems Symposium (RTSS)</emphasis>, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Regnier%2C+P%2E%2C+Lima%2C+G%2E%2C+Massa%2C+E%2E%2C+Levin%2C+G%2E%2C+and+Brandt%2C+S%2E%2C+%22RUN%3A+Optimal+multiprocessor+real-time+scheduling+via+reduction+to+uniprocessor%2C%22+IEEE+32nd+Real-Time+Systems+Symposium+%28RTSS%29%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Fisher, N., Goossens, J., and Baruah, S., Optimal online multiprocessor scheduling of sporadic real-time tasks is impossible. <emphasis>Real-Time Sys. J.</emphasis> 45.1-2, 26&#x02013;71, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Fisher%2C+N%2E%2C+Goossens%2C+J%2E%2C+and+Baruah%2C+S%2E%2C+Optimal+online+multiprocessor+scheduling+of+sporadic+real-time+tasks+is+impossible%2E+Real-Time+Sys%2E+J%2E+45%2E1-2%2C+26-71%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Melani, A., Bertogna, M., Bonifaci, V., Marchetti-Spaccamela, A., and Buttazzo, G., &#x0201C;Response-Time Analysis of Conditional DAG Tasks in Multiprocessor Systems,&#x0201D; in <emphasis>27th Euromicro Conference on Real-Time Systems, ECRTS 2015</emphasis>, Lund, Sweden, pp. 7&#x02013;10, 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Melani%2C+A%2E%2C+Bertogna%2C+M%2E%2C+Bonifaci%2C+V%2E%2C+Marchetti-Spaccamela%2C+A%2E%2C+and+Buttazzo%2C+G%2E%2C+%22Response-Time+Analysis+of+Conditional+DAG+Tasks+in+Multiprocessor+Systems%2C%22+in+27th+Euromicro+Conference+on+Real-Time+Systems%2C+ECRTS+2015%2C+Lund%2C+Sweden%2C+pp%2E+7-10%2C+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 3.3.2. <emphasis>Enhanced scheduler with migration support</emphasis>. Delivery date: 31 March 2016.</para></listitem>
<listitem>
<para>Serrano, M. A., Melani, A., Bertogna, M., and Qui&#x000F1;ones, E., &#x0201C;Response-Time Analysis of DAG Tasks under Fixed Priority Scheduling with Limited Preemptions,&#x0201D; in the <emphasis>Design, Automation, and Test in Europe conference (DATE)</emphasis>, Dresden, Germany, pp. 14&#x02013;18, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Serrano%2C+M%2E+A%2E%2C+Melani%2C+A%2E%2C+Bertogna%2C+M%2E%2C+and+Qui%F1ones%2C+E%2E%2C+%22Response-Time+Analysis+of+DAG+Tasks+under+Fixed+Priority+Scheduling+with+Limited+Preemptions%2C%22+in+the+Design%2C+Automation%2C+and+Test+in+Europe+conference+%28DATE%29%2C+Dresden%2C+Germany%2C+pp%2E+14-18%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 4.2.2. <emphasis>Interference Model</emphasis>. Delivery date: 31 March 2016.</para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 1.5.2. <emphasis>Integrated Tool-chain</emphasis>. Delivery date: 31 March 2016.</para></listitem>
<listitem>
<para>Blumofe, R. D., and Leiserson, C. E., Scheduling multithreaded computations by work stealing. <emphasis>J. ACM</emphasis> 46, 720&#x02013;748, 1999. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Blumofe%2C+R%2E+D%2E%2C+and+Leiserson%2C+C%2E+E%2E%2C+Scheduling+multithreaded+computations+by+work+stealing%2E+J%2E+ACM+46%2C+720-748%2C+1999%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Serrano, M. A., Melani, A., Kehr, S., Bertogna, M., and Qui&#x000F1;ones, E., &#x0201C;An Analysis of Lazy and Eager Limited Preemption Approaches under DAG-based Global Fixed Priority Scheduling,&#x0201D; in the <emphasis>19th IEEE International Symposium on Object/Component/Service-oriented Real-time Distributed Computing (ISORC)</emphasis>, Toronto, Canada, pp. 16&#x02013;18, 2017. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Serrano%2C+M%2E+A%2E%2C+Melani%2C+A%2E%2C+Kehr%2C+S%2E%2C+Bertogna%2C+M%2E%2C+and+Qui%F1ones%2C+E%2E%2C+%22An+Analysis+of+Lazy+and+Eager+Limited+Preemption+Approaches+under+DAG-based+Global+Fixed+Priority+Scheduling%2C%22+in+the+19th+IEEE+International+Symposium+on+Object%2FComponent%2FService-oriented+Real-time+Distributed+Computing+%28ISORC%29%2C+Toronto%2C+Canada%2C+pp%2E+16-18%2C+2017%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Anderson, T. E., &#x0201C;The performance of spin lock alternatives for shared-memory multiprocessors.&#x0201D; In <emphasis>IEEE Transactions on Parallel and Distributed Systems</emphasis>, 1990. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Anderson%2C+T%2E+E%2E%2C+%22The+performance+of+spin+lock+alternatives+for+shared-memory+multiprocessors%2E%22+In+IEEE+Transactions+on+Parallel+and+Distributed+Systems%2C+1990%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Craig, T. S., &#x0201C;Queuing spin lock algorithms to support timing predictability.&#x0201D; In <emphasis>Proc. Real-Time Sys. Symp</emphasis>. pp. 148&#x02013;157, 1993. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Craig%2C+T%2E+S%2E%2C+%22Queuing+spin+lock+algorithms+to+support+timing+predictability%2E%22+In+Proc%2E+Real-Time+Sys%2E+Symp%2E+pp%2E+148-157%2C+1993%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Shen, C., Molesky, L. D., and Zlokapa, G., &#x0201C;Predictable synchronization mechanisms for real-time systems.&#x0201D; In <emphasis>Real-Time Systems</emphasis>, 1990. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Shen%2C+C%2E%2C+Molesky%2C+L%2E+D%2E%2C+and+Zlokapa%2C+G%2E%2C+%22Predictable+synchronization+mechanisms+for+real-time+systems%2E%22+In+Real-Time+Systems%2C+1990%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Graunke, G., and Thakkar, S., &#x0201C;Syncronization algorithms for shared-memory multiprocessors.&#x0201D; In <emphasis>IEEE Computer</emphasis>, 1990. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Graunke%2C+G%2E%2C+and+Thakkar%2C+S%2E%2C+%22Syncronization+algorithms+for+shared-memory+multiprocessors%2E%22+In+IEEE+Computer%2C+1990%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Melani, A., Serrano, M. A., Bertogna, M., Cerutti, I., Qui&#x000F1;ones, E., and Buttazzo, G., &#x0201C;A static scheduling approach to enable safety-critical OpenMP applications,&#x0201D; in the <emphasis>21st Asia and South Pacific Design Automation Conference (ASP-DAC)</emphasis>, Tokyo, Japan, pp. 16&#x02013;19, 2017. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Melani%2C+A%2E%2C+Serrano%2C+M%2E+A%2E%2C+Bertogna%2C+M%2E%2C+Cerutti%2C+I%2E%2C+Qui%F1ones%2C+E%2E%2C+and+Buttazzo%2C+G%2E%2C+%22A+static+scheduling+approach+to+enable+safety-critical+OpenMP+applications%2C%22+in+the+21st+Asia+and+South+Pacific+Design+Automation+Conference+%28ASP-DAC%29%2C+Tokyo%2C+Japan%2C+pp%2E+16-19%2C+2017%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 3.1. <emphasis>Resource Allocation Requirements</emphasis>. Delivery date: 30 April 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=P-SOCRATES+Deliverable+3%2E1%2E+Resource+Allocation+Requirements%2E+Delivery+date%3A+30+April+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 5.2. <emphasis>Operating Systems Support Prototypes</emphasis>. Delivery date: 31 March 2015.</para></listitem>
<listitem>
<para>P-SOCRATES Annex I &#x02013; <emphasis>Description of Work</emphasis>, 2014.</para></listitem>
<listitem>
<para>P-SOCRATES Deliverable 3.2. <emphasis>Mapping Strategies</emphasis>. Delivery date: 31 March 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=P-SOCRATES+Deliverable+3%2E2%2E+Mapping+Strategies%2E+Delivery+date%3A+31+March+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Joseph, M., Pandya, P., Finding response times in a real-time system. <emphasis>Comput. J.</emphasis> 29, 390&#x02013;395, 1986. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Joseph%2C+M%2E%2C+Pandya%2C+P%2E%2C+Finding+response+times+in+a+real-time+system%2E+Comput%2E+J%2E+29%2C+390-395%2C+1986%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Maia, C., Nogueira, L., and Pinho, L. M., &#x0201C;Scheduling Parallel Real-Time Tasks using a Fixed-Priority Work-Stealing Algorithm on Multiprocessors,&#x0201D; in <emphasis>8</emphasis><superscript><emphasis>th</emphasis></superscript> <emphasis>IEEE Symposium on Industrial Embedded Systems</emphasis>, Porto, Portugal, pp. 19&#x02013;21, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Maia%2C+C%2E%2C+Nogueira%2C+L%2E%2C+and+Pinho%2C+L%2E+M%2E%2C+%22Scheduling+Parallel+Real-Time+Tasks+using+a+Fixed-Priority+Work-Stealing+Algorithm+on+Multiprocessors%2C%22+in+8th+IEEE+Symposium+on+Industrial+Embedded+Systems%2C+Porto%2C+Portugal%2C+pp%2E+19-21%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch05" label="5" xreflabel="5">
<title>Timing Analysis Methodology</title>
<para><emphasis role="strong">Vincent N&#x000E9;lis, Patrick Meumeu Yomsi and Lu&#x000ED;s Miguel Pinho</emphasis></para>
<para>CISTER Research Centre, Polytechnic Institute of Porto, Portugal</para>
<para>This chapter focuses on the analysis of the timing behavior of software applications that expose real-time (RT) requirements. The state-of-the-art methodologies to timing analysis of software programs are generally split into four categories, referred to as <emphasis>static</emphasis>, <emphasis>measurement-based</emphasis>, <emphasis>hybrid</emphasis>, and <emphasis>probabilistic analysis</emphasis> techniques. First, we present an overview of each of these methodologies and discuss their advantages and disadvantages. Next, we explain the choices made by our proposed methodology in Section 5.2 and present the details of the solution in Section 5.3. Finally, we conclude the chapter in Section 5.4 with a summary.</para>
<section class="lev1" id="sec5-1">
<title>5.1 Introduction</title>
<para>Most of the timing analysis tools focus only on determining an upper-bound on the Worst-Case Execution Time (WCET) of a program or function code that runs in isolation and without interruption. In other words, these tools do not consider all the interferences that the execution of the analyzed code may suffer when it runs concurrently with other tasks or programs on the same hardware platform. They typically ignore all execution interferences due to the contention for shared software resources (e.g., data shared between several tasks) and shared hardware resources (e.g., shared interconnection network) <footnote id="fn5_1" label="1"> <para>Note that the OTAWA timing analysis tool is able to analyze parallel code with synchronization primitives [1].</para></footnote> [1]. Interferences from the operating system (OS) which frequently re-schedules and interrupts the programs are also ignored by WCET analyzers. All these interactions between the analyzed task, the OS, and all the other tasks running in the system are assessed separately and sometimes they are incorporated into a higher-level schedulability analysis. For the timing requirements to be fulfilled, it is neither acceptable nor realistic to ignore these sources of contention and interference at the schedulability-analysis level.</para>
<para>WCET analysis can be performed in a number of ways using different tools, but the main methodologies employed can be classified into four categories:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Static analysis techniques</para></listitem>
<listitem>
<para>Measurement-based analysis techniques</para></listitem>
<listitem>
<para>Hybrid analysis techniques</para></listitem>
<listitem>
<para>Measurement-based probabilistic analysis techniques</para></listitem>
</orderedlist>
<para>Note that the first three methodologies are usually acknowledged as equally important and efficient as they target different types of applications. In addition, they are not comparable in the sense that one technique has not been proven to dominate the others. The fourth technique is more recent and thus fewer results are available.</para>
<para><emphasis>Measurement-based</emphasis> techniques are suitable for software that is less time-critical and for which the average-case behavior (or a rough WCET estimate) is more meaningful or relevant than an accurate estimate like, for example, in systems where the worst-case scenario is extremely unlikely to occur. For highly time-critical software, where every possible execution scenario must be covered and analyzed, the WCET estimate must be as reliable as possible and <emphasis>static</emphasis> or <emphasis>hybrid</emphasis> methods are therefore more appropriate. <emphasis>Measurement-based probabilistic analysis</emphasis> techniques are also designed for safety-critical systems to derive safe estimated execution time bounds, but they are not yet sufficiently mature to report on their efficiency and applicability. Indeed, a consensus is still to be reached in the research community on this matter.</para>
<fig id="F5-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F5-1">Figure <xref linkend="F5-1" remap="5.1"/></link></label>
<caption><para>Example distribution of execution time (picture taken from [2]).</para></caption>
<graphic xlink:href="graphics/ch05_fig5_1.jpg"/>
</fig>
<para>For the execution time of a single <emphasis>sequential</emphasis> program run <emphasis>in isolation</emphasis>, <link linkend="F5-1">Figure <xref linkend="F5-1" remap="5.1"/></link> shows how different timing estimates relate to the WCET and best-case execution time (BCET). The example program has a variable execution time that depends on (1) its input parameters and (2) its interactions with the system resources. The darker curve shows the actual probability distribution of its execution time; its minimum and maximum are the BCET and WCET respectively. The lower grey curve shows the set of execution times that have been observed and measured during simulations, which is a subset of all executions; its minimum and maximum are the <emphasis>minimal measured time</emphasis> and <emphasis>maximal measured time</emphasis>, respectively. For both static analysis tools and measurements-based tools, in most cases the program state space and the hardware complexity are too large to exhaustively explore all possible execution scenarios of the program. This means that <emphasis>the measured times are likely to be optimistic</emphasis> and the <emphasis>estimated times are likely to be pessimistic</emphasis> &#x02013; i.e., the measured times will in many cases overestimate the actual BCET and underestimate the actual WCET, while the approximated estimated times will in many cases underestimate the actual BCET and overestimate the actual WCET.</para>
<para>The next four subsections introduce each of the four timing-analysis methodologies and discuss their potential advantages and disadvantages.</para>
<section class="lev2" id="sec5-1-1">
<title>5.1.1 Static WCET Analysis Techniques</title>
<para>Static WCET analysis is usually performed in three conceptual and possibly overlapping phases.</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis role="underline">A flow analysis phase</emphasis> in which information about the possible program execution paths is derived. This step builds a control flow-graph from the given program with the aim of identifying the worst path (in terms of execution time).</para></listitem>
<listitem>
<para><emphasis role="underline">A low-level analysis phase</emphasis> during which information about the execution time of atomic parts of the code (e.g., instructions, basic blocks, or larger code sections) is obtained from a model of the target architecture.</para></listitem>
<listitem>
<para><emphasis role="underline">A final calculation phase</emphasis> in which the derived flow and timing information are combined into a resulting WCET estimate.</para></listitem>
</orderedlist>
<para><emphasis>Flow analysis</emphasis> mostly focuses on loop bound analyses, hence upper-bounds on the number of iterations in each looping structure must be known to derive WCET estimates. Similarly, recursion depth must also be bounded. Automatic methods to find these bounds have been proposed by the research community but for many available tools, some annotations on the maximum number of iterations in a loop must be provided manually in the code of the tasks by the application developer. Another purpose of flow analysis is to identify infeasible execution paths, which are paths that are executable according to the control-flow graph but are not feasible when considering the semantics of the program and the possible input data values. Discarding unfeasible paths at an early stage of the analysis considerably reduces the search space when trying to identify the longest path.</para>
<para><emphasis>Low-level analysis</emphasis> methods typically use models of all the hardware components and their arbitration policies, including CPU caches, cache replacement policies, write policies, instruction pipeline, memory bus and their arbitration policies, etc. These models are typically expressed in the form of complex mathematical abstractions for which a worst-case operation can be estimated.</para>
<para><emphasis role="strong">Pros:</emphasis> There are a few advantages of using static analysis techniques that rely on mathematical models.</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>It eliminates the need for having the actual hardware available, which removes the cost of acquiring and setting up the target platform.</para></listitem>
<listitem>
<para>It enables safe WCET upper-bounds to be derived without running the program on the target platform while still considering the influence of the state changes in the underlying hardware [3]. State changes include, e.g., a cache line being evicted, a pipeline being totally flushed out, etc.</para></listitem>
</itemizedlist>
<para><emphasis role="strong">Cons:</emphasis> On the downside, we shall note the following drawbacks.</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>These approaches rely heavily on having an accurate model of the timing behavior of all the target hardware components and management policies, including modeling features like pipelines and caches that substantially affect the execution time of the task being executed. Although the embedded market used to be traditionally dominated by simple and predictable processors (which used to be moderately &#x0201C;easy&#x0201D; to model and allowed for deriving safe and tight bounds), with the increased computational needs of modern embedded systems, designers have moved to more complex processors which are now mainly designed for performance and not for predictability. For this new generation of processors, designing an accurate hardware model is very challenging, as all the intricacies contributing to the variation in the task execution times (e.g., caches, pipelines, out-of-order execution, branch prediction, automatic hardware prefetching, etc.) should be captured by the model to provide safe and sufficiently tight bounds. Because it is hardly feasible to accurately model all these acceleration mechanisms and their operation, static methods typically forbid their use and are struggling to adapt to modern hardware architectures.</para></listitem>
<listitem>
<para>Besides the difficulty of modeling all these performance-enhancement hardware features, it must also be noted that generally, chip manufacturers do not publish the details of their internal workings, which further complicates/makes impossible the design of an accurate model.</para></listitem>
<listitem>
<para>Although static approaches have the advantage of providing safe WCET bounds, they can be very pessimistic at times. This is because generally, each hardware resource is modeled separately, and all the worst-case estimates are then composed together to form the final WCET bound. However, at runtime, it is often impossible for all these individual worst-case scenarios to happen at the same time.</para></listitem>
<listitem>
<para>The hardware model must be thoroughly verified to ensure that it indeed reflects the target hardware; failing to capture inherent performance enhancing features may result in overestimations of the execution times, whereas capturing all system states in a complex machine may lead to unacceptably long analysis times. Building and verifying the timing model for each processor variant is expensive, time consuming, and error prone. Custom variants and different versions of processors often have subtly different timing behaviors, rendering timing models either incorrect or unavailable.</para></listitem>
</itemizedlist>
<para>It is very important to stress at this point that static analysis techniques have been designed primarily to analyze simple software codes meant to run on simple and predictable hardware architectures. These targeted codes are typically implemented by using high-level programming languages and by obeying strict and specific coding rules to reduce the likelihood of programmer error.</para>
<para>The modeling framework adopted by static analysis lends itself to formal proofs which help in establishing whether the obtained results are safe. Today, there are several static WCET tools that are commercially available, including <emphasis>aiT</emphasis> [4] and <emphasis>Bound-T</emphasis> [5]. Note that Bound-T is no longer actively developed due to both commercial and technical reasons. We redirect the interested reader to their website (<ulink url="http://www.bound-t.com/">http://www.bound-t.com/</ulink>) for further details on this matter. There also exist several research prototypes, including <emphasis>Chronos</emphasis> [6], developed at National University of Singapore, <emphasis>Heptane</emphasis> [7], developed at the French National Institute for Research in Computer Science and Control (INRIA) IRISA in France, <emphasis>SWEET</emphasis> [8], developed at M&#x000E4;lardalen Real-Time Research Center (MRTC) in Sweden, and OTAWA [9] from IRIT in France.</para>
</section>
<section class="lev2" id="sec5-1-2">
<title>5.1.2 Measurement-based WCET Analysis Techniques</title>
<para>The traditional and most common method in the industry to determine program timing is by measurements. The basic principle of this method follows the mantra that &#x0201C;the processor is the best hardware model.&#x0201D; The program is executed many times on the actual hardware, with different inputs and <emphasis>in isolation</emphasis>, and the execution time is measured for each run by instrumenting the source code at different points [10]. Each measurement run exercises only one execution path throughout the program, and thus for the same set of input values, several thousands of program runs must be carried out to capture variations in execution time due to the fluctuation in system states. For those measurement-based approaches, the main challenge is essentially to identify the set of input arguments of the application that leads to its WCET.</para>
<para><emphasis role="strong">Pros:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Measurements are often immediately at the disposal of the programmer, and are useful mainly when the average case-timing behavior or an approximate WCET value is of interest.</para></listitem>
<listitem>
<para>Most types of measurements have the advantage of being performed on the actual hardware, which avoids the need to construct a hardware model and hence reduces the overall cost of deriving the estimates.</para></listitem>
</itemizedlist>
<para><emphasis role="strong">Cons:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Measurements require that hardware is available, which might not be the case for systems for which the hardware is developed in parallel with the software.</para></listitem>
<listitem>
<para>It may be problematic to set up an environment which acts like the final system.</para></listitem>
<listitem>
<para>The integrity of the actual code to be deployed in the target hardware is somehow depleted by the addition of the intrusive instrumentation code to measure the time, i.e., the measurements themselves add to the execution time of the analyzed program. This problem can be reduced, e.g., by using hardware measurement tools with no or very small intrusiveness, or by simply letting the added measurement code (and thus the extra execution time) remain in the final program. When doing measurements, possible disturbances, e.g., interrupts, also have to be identified and compensated for.</para></listitem>
<listitem>
<para>For most programs, the number of possible execution paths is too large to do exhaustive testing and therefore, measurements are carried out only for a subset of the possible input values, e.g., by giving potential &#x0201C;nasty&#x0201D; inputs which are likely to provoke the WCET, based on some manual inspection of the code. Unfortunately, the measured times will in many cases underestimate the WCET, especially when complex software and/or hardware are being analyzed. To compensate for this, it is common to add a safety margin to the worst-case measured timing, in the hope that the actual WCET lies below the resulting WCET estimate. The main issue is whether the extra safety margin provably provides a safe bound, since it is based on some informed estimates. A very high margin will result in resource over-dimensioning, leading to very low utilization while a small margin could lead to an unsafe system.</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec5-1-3">
<title>5.1.3 Hybrid WCET Techniques</title>
<para>Hybrid approaches, as the name implies, present the advantages of both static and measurement-based analysis techniques. Firstly, they borrow the flow-analysis phase from static methods to construct a control flow-graph of the given program and identify a set of feasible and potentially worst execution paths (in terms of execution time). Next, unlike static methods that use mathematical models of the hardware components, hybrid tools borrow their second phase from measurement-based techniques and determine the execution time of those paths by executing the application on the target hardware platform or by cycle-accurate simulators. To do so, the source code of the application is instrumented with expressions (instrumentation points) that indicate that a specific section of code has been executed. These instrumentation points are typically placed along the paths identified in the first phase as leading to a WCET. The application is then executed on the target hardware platform or on the simulator to collect execution traces. These traces are a sequence of time-stamped values that show which parts of the application has been executed. Finally, hybrid tools produce performance metrics for each part of the executed code and, by using the performance data and knowledge of the code structure, they estimate the WCET of the program.</para>
<para><emphasis role="strong">Pros:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Hybrid approaches do not rely on complex abstract models of the hardware architecture.</para></listitem>
<listitem>
<para>They generally provide safe WCET estimates (i.e., higher than the actual WCET) and those are very often tighter than the estimates returned by static approaches (i.e., closer to the actual WCET).</para></listitem>
</itemizedlist>
<para><emphasis role="strong">Cons:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>The uncertainty of covering the worst-case behavior by the measurement remains since it cannot be guaranteed that the maximum interference and the worst-case execution scenario has been experienced when collecting the traces during the second phase.</para></listitem>
<listitem>
<para>It is required to instrument the application source code, which poses the same issue of intrusiveness as in measurement-based approaches. Example tools include Rapitime [11] and MTime [12].</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec5-1-4">
<title>5.1.4 Measurement-based Probabilistic Techniques</title>
<para>With the current hardware designs, the execution time of a given application depends on the states of the hardware components, and those states depend in turn on what has been executed previously. A classic example of such a tight relationship between the application and the underlying hardware architecture is the execution time discrepancy that can be observed when a program executes on a processor equipped with a cache subsystem. During the first execution of the program, every request to fetch instructions and data results in a cache miss and must be loaded from the main memory. At the second execution, this information is already in the cache and need not be reloaded from the memory, which results in an execution time considerably shorter than during the first run. Because of this dependence to past events, the set of measured execution times of the same program cannot be seen as a set of IID (independent and identically distributed) random variables and most statistical tools cannot be applied to analyze the collected execution traces.</para>
<para>The objective of measurement-based probabilistic techniques is to break this dependence on past events, so that one can sample the execution behavior of an application and then derive from the sample probabilistic estimates (of any parameter) that apply to its overall behavior, under all circumstances and in all situations. To achieve this goal, researchers are nowadays working on modifying the hardware components and their arbitration policies to make them behave in a stochastic manner, without losing too much of their performance. For example, by replacing the traditional Least Recently Used (LRU) or Pseudo-LRU (PLRU) cache-replacement policy for a policy that randomly chooses the cache line to be evicted (and assuming that every cache line has the same probability of getting evicted), the time overhead due to cache penalties and cache line evictions can be analyzed as an IID random variable with a known distribution. If every source of interference exhibits a randomized behavior with a known distribution, then the execution time itself can be analyzed statically.</para>
<para>The current trend in probabilistic approaches is to apply results from the extreme value theory (EVT) framework to the WCET estimation problem [12, 13]. In a nutshell, these EVT-based solutions first sample the execution time of an application by running it over multiple sets of input arguments on a randomized architecture that is designed to confer a stochastic behavior on the application runtime. Then, these EVT-based solutions organize the sample into multiple groups/intervals, analyze the distribution of the local maxima within these intervals and then estimate how far the execution time may deviate from the average of that &#x0201C;distribution of the extremes.&#x0201D;</para>
<para>Although considerably new, measurement-based probabilistic techniques have been the object of tremendous research efforts in the last few years, most of the breakthroughs in that discipline have been made in the scope of the European projects PROARTIS [14] and PROXIMA [15].</para>
<para><emphasis role="strong">Pros:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Provide safe and potentially tighter WCET estimates than static and hybrid techniques.</para></listitem>
<listitem>
<para>Provide information not only on the WCET of a program but on the complete spectrum of the distribution of its execution time.</para></listitem>
</itemizedlist>
<para><emphasis role="strong">Cons:</emphasis></para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Require modifying the hardware to ensure that the components exhibit a stochastic behavior.</para></listitem>
<listitem>
<para>As the IID requirement is hardly verified in currently available platforms (especially COTS platforms), the applicability of measurement-based probabilistic techniques is limited.</para></listitem>
</itemizedlist>
</section>
</section>
<section class="lev1" id="sec5-2">
<title>5.2 Our Choice of Methodology for WCET Estimation</title>
<para>As seen in the previous section, there exist several methodologies to estimate the WCET of an application, each with its own advantages and disadvantages. Those methodologies fall into the following main categories, namely static, measurement-based, and hybrid. Here we would like to briefly re-iterate on why among those four methodologies we decided to use a measurement-based approach.</para>
<para>There is currently an evident clash of opinions in the research community about which methodology prevails over the others. During the last two years we had the opportunity to debate with partisans of each of these approaches. It is important to stress that we do not mean to take a side in this book, simply because we recognize that each approach comes with its own set of strengths and weaknesses. Our methodology simply uses the one whose downsides impede as little as possible our objectives. The following subsections summarize our opinion on the matter and present the observations that have driven our choice towards using a measurement-based technique.</para>
<section class="lev2" id="sec5-2-1">
<title>5.2.1 Why Not Use Static Approaches?</title>
<para>In this section, we present some of the reasons why we did not choose static approaches to timing analysis, but rather opted for a measurement-based approach. Before going into the details, it is worth mentioning that recent COTS manycore platforms present complex and sophisticated architectures such that it is very challenging at design time, if not impossible, to come up with an accurate model for all the behavioral implications associated with the possible operational decisions that the system can take at runtime. This claim holds true even for the most experienced systems designers.</para>
<blockquote>
<para><emphasis>By using hardware platforms such as the Kalray MPPA-256, or any other platform designed to provide high performance, we argue that it is practically infeasible to derive WCET estimates by using static timing analysis techniques.</emphasis></para>
</blockquote>

<para>In theory, it is always possible to extract safe and reliable timing models and define mathematical abstractions to study the behavior of a deterministic system. However, we argue that it is practically challenging to define and use static mathematical models of the considered platforms, mainly because of:</para>
<para><emphasis role="strong">The inherent system complexity:</emphasis> Typical COTS hardware components are extremely complex. Currently the market of embedded and electronic components is unarguably driven by the ever-increasing need for higher performance. The only way to constantly enhance the performance is to optimize the produced chips and boards by adding all sorts of optimization features. Optimization is achieved by allowing the system to take and revise its operational decisions on-the-fly, at runtime, based on the current workload of the system or any informational data collected about the running application and its environment. Since those decisions are taken at runtime, it is impossible to predict the exact behavior of the system at the analysis time. The only option for static tools is to assume that the system will most of the time be in a worst-case situation, in which the optimization features will have very little or no effect. This makes static models pessimistic and the produced timing estimates may not reflect accurately the actual timing behavior of the system.</para>
<para><emphasis role="strong">The human resources required:</emphasis> An increased system complexity leads to a longer time-to-model. Developing a draft model of a platform may take up to several years to reach the desired level of accuracy and be validated. Besides this fact, our software stack and methodology aim at being platform agnostic and therefore be applicable to a large set of hardware platforms. To this end, they should provide a generic abstraction between the application logic and the system interfaces so that the development costs and efforts are always reasonable and limited. This is an objective for which the inherent portability of measurement-based solutions appears to be more appropriate.</para>
<para><emphasis role="strong">Portability: The &#x0201C;rigidity&#x0201D; of static approaches:</emphasis> Using static timing analysis techniques goes against our goal of developing a flexible and generic framework which can be &#x0201C;easily&#x0201D; ported to different platforms from various vendors. This has been a key driver in the development of our timing analysis methods, in order to increase the exploitation opportunities in multiple application domains.</para>
<para><emphasis role="strong">The non-availability of the specification details:</emphasis> To devise accurate models, all the information about the target platform must be available and accurate. This is not the case in practice. Chip manufacturer generally keep most information secret, unfortunately.</para>
<para><emphasis role="strong">The complexity of the execution environment:</emphasis> Static timing analysis tools are designed primarily to focus on applications executed <emphasis>sequentially</emphasis> in safety-critical embedded systems. Those systems generally provide a very time-predictable and &#x0201C;inflexible&#x0201D; runtime environment in which every mapping and scheduling decision is statically taken at design-time and is then final. Unlike those systems, the software stack considered in this book offers a much more complex and dynamic runtime environment composed of multiple conceptual layers: the code of the RT tasks is executed <emphasis>in parallel</emphasis> by being fractioned into OpenMP tasks, those tasks are mapped to clusters, then to threads inside the clusters, and then these threads are scheduled statically or dynamically on the cores. The dynamicity of the processor resource usage ensures a decent application throughput (by maximizing the utilization of the available computing resources) but it naturally impacts adversely on its time-predictability.</para>
<blockquote>
<para><emphasis>Traditional hybrid approaches are also not applicable as the complexity of the software stack makes the static control-flow analysis step impossible.</emphasis></para>
</blockquote>
<para>Since the RT tasks execute in parallel, and even using static mapping approaches, the total order of execution of task-parts is only determined at runtime, it is thus infeasible to investigate all possible scenarios at design-time to identify the worst-case execution flow/path. It is important to re-iterate that traditional timing analysis techniques have been designed primarily to analyze &#x0201C;simple&#x0201D; software codes executed on &#x0201C;simple&#x0201D; and predictable hardware architectures, typically implemented by using low-level programming languages and by obeying strict and specific coding rules to reduce programmer&#x02019;s errors. The framework presented in this book clearly targets much more complex software applications that exhibit a high degree of flexibility and dynamicity in their execution.</para>
</section>
<section class="lev2" id="sec5-2-2">
<title>5.2.2 Why Use Measurement-based Techniques?</title>
<blockquote>
<para><emphasis>In measurement-based approaches, WCET estimations are derived from values that have been observed during the experimentation. What about the values that have not been observed? How can we account for them and be sure that the WCET estimates are reliable?</emphasis></para>
</blockquote>
<para>Critics of measurement-based approaches for estimating the WCET of an application make a simple yet very valid point. The actual WCET is unknown and is very likely not to be experienced during testing. Even worse, it is not even possible to know whether the worst case has been observed or not. In short, this means that there is no guarantee that such an approach can forecast the exact value of the WCET. All measurement-based techniques implicitly infer a WCET from values for which the &#x0201C;distance&#x0201D; from the actual worst-case is unknown. A direct consequence is that, although those techniques make predictions based on sophisticated and elaborate computations, formally speaking, they can never guarantee that their predictions are 100% &#x0201C;safe&#x0201D;. This may be problematic for applications requiring hard RT guarantees, typically in safety-critical systems for instance.</para>
<para>However, one can note that in many application domains, certifiable guarantees based on unquestionable and provable arguments are not required. For instance, many applications need only &#x0201C;reliable&#x0201D; estimations, in the sense that one must be able to rely on those values and measure the risk of them being wrong (through confidence levels provided by the analysis, for example).</para>
<para>Estimations of the trustworthiness of the produced values (i.e., the confidence in those values) can be expressed through probabilities derived by statistical tools. Specifically, in our approach, the traces of execution times collected at runtime are fed into a statistical framework, called DiagXtrm, in which they are subjected to a set of tests to verify basic statistic hypotheses, such as stationarity, independence, extremal independence, execution patterns/modes, etc. Depending on the results of those tests, it is determined whether the EVT can be applied to those traces. If the tests are successful, the EVT is used to &#x0201C;extrapolate&#x0201D; the recorded execution times and accurately identify the higher values that have not been observed during testing, but for which the likelihood of occurrence is not statistically impossible. Besides this, our framework also provides techniques to assess how &#x0201C;trustworthy&#x0201D; those EVT estimations really are. This last step is of fundamental importance to evaluate the quality of the estimations and find out whether confidence can be placed into the analysis.</para>
<para>Despite all the interesting features provided by the application of EVT to the WCET determination problem, it has been widely criticized in the research community. The main argument against it is that the process of creating the traces (i.e., the execution of an application&#x02019;s code by a given hardware platform) is known to be a process which is neither independent nor identically distributed, which is a prerequisite to the application of the EVT to a data sample. We believe that this argument, although correct because the process is <emphasis>de facto</emphasis> not inherently IID, does not allow to conclude on the non-applicability of the EVT. In our view, being an IID process is not necessary, provided that the said process behaves as if it were. This is why the EVT has been applied in so many application domains where it is today recognized to provide helpful and satisfactory results. EVT is used for instance to predict the probability distribution of the amount of large insurance losses, day-to-day market risk, and large wildfires. Needless to say, none of these processes are truly IID.</para>
<para>Whether this is right or not is disputable and we do not intend to close the discussion in this chapter. However, we believe that the doubt this casts on the applicability of the EVT makes this framework worth being investigated further and hopefully will unveil its true potential. In case we are wrong, we will hopefully discover why it is not applicable and close the debate that has been going on already for several years.</para>
<blockquote>
<para><emphasis>In measurement-based approaches, the integrity of the actual code to be deployed in the target hardware is somehow depleted by the addition of the intrusive instrumentation code to measure the time; in other words, the measurements themselves add an overhead to the execution time of the analyzed program.</emphasis></para>
</blockquote>
<para>This problem can be reduced, e.g., by using hardware measurement tools with no or very small intrusiveness, or by simply letting the added measurement code (and thus the extra execution time) remain in the final program. When doing this, possible disturbances like interrupts also have to be identified and compensated for. The intrusiveness of the instrumentation code is discussed in Section 5.3.5 and we provide efficient solutions to deal with it.</para>
<para>Nearly all the embedded platforms, like the MPPA-256 platform considered in our experimentations, provide a lightweight and non-intrusive trace system that enables the collection of execution traces in predefined time bounds. By using this trace system, we are able to collect meaningful traces of execution without generating too many disturbances in the regular timing behavior of the analyzed application. Based on all the experiments conducted on the Kalray board, we concluded that the time necessary to record a time stamp is 52 clock cycles. By placing &#x0201C;trace-points&#x0201D; (points in the program where the current time is recorded) at well-defined places, we can thus easily subtract the overhead associated with measuring the time itself.</para>
<para><emphasis>Wrapping things up:</emphasis></para>
<para>The best candidates for the worst-case timing analysis of the type of workloads considered in this book are the measurement-based approaches. Thus, our proposed methodology relies on timing-related data collected by running the application on the target hardware. This way, we avoid both the burden of modeling the various hardware components (which takes considerable effort and time), as in static timing analysis tools; and the pitfalls and pessimism associated with the over-approximations resulting from the confidentiality, and thus the non-availability, of specific information related to the internal configuration of the components. In addition, the fact that our approach is not tied to specific hardware infrastructures and application designs allows it to benefit from a higher flexibility and portability than static timing analysis methods, and it considerably reduces the time-to-model and time-to-result. In the next sections, we will discuss the specifics of our method and how we propose to overcome or at least mitigate the negative aspects inherent to measurement-based techniques.</para>
</section>
</section>
<section class="lev1" id="sec5-3">
<title>5.3 Description of Our Timing Analysis Methodology</title>
<section class="lev2" id="sec5-3-1">
<title>5.3.1 Intrinsic vs. Extrinsic Execution Times</title>
<para>The execution time of any piece of code, e.g., a basic block, a software function, or an <emphasis>OpenMP task-part</emphasis>, can be seen as composed of two main terms: the <emphasis>intrinsic</emphasis> execution time spent executing the instructions of the code, and the <emphasis>stalling</emphasis> time, i.e., the time spent waiting for a shared software or hardware resource to become available. To understand how timing analysis is performed in this book, it is fundamental to understand the difference between these two components. If the analyzed software function does not have a functional random behavior (i.e., the outcome of evaluating a condition is never the result of an operation involving randomly generated numbers), then any input dataset always produces one output (and this output remains the same no matter how many times the function is executed on the same input). Further, for a given input dataset, the execution path taken throughout the function&#x02019;s code will always be the same. That is, under this assumption of not involving randomness in the control flow of the analyzed function, running it over a given set of input data over and over again always results in executing the exact same sequence of instructions and eventually, it always produces the same output.</para>
<para>For a given input dataset, we call the &#x0201C;<emphasis>intrinsic</emphasis> execution time&#x0201D; of a function the time that it takes to produce its output, assuming that all software and hardware services provided by the execution environment and shared among different cores are always available, and thus the core running that function never stalls waiting for one of these resources to become available. That is, the intrinsic execution time of a function is its execution time when it runs in isolation, i.e., with no interference whatsoever with the rest of the system on the shared resources. On a perfectly predictable hardware architecture where every instruction takes a constant number of cycles to execute, running the same function in isolation over the same set of input arguments should always results in the exact same execution time. Although this may sound like a very strong assumption, we will see that on a platform such as the Kalray MPPA-256 this property is satisfied. By running a preliminary set of tests with the same program an arbitrary number of times over the same inputs, we experienced a variation of its execution time of typically less than 0.1% of the maximum observed.</para>
<para>For a given input dataset, we call the &#x0201C;<emphasis>extrinsic</emphasis> execution time&#x0201D; of a function the time that it takes to produce its output, assuming a maximum interference on all the shared resources. That is, the extrinsic execution time of a function is its execution time assuming that all the software and hardware services provided by the execution environment and shared among the cores are constantly saturated by concurrent requests from other system components. Contrary to the intrinsic execution time, on mainstream multicore architectures the extrinsic execution time is subject to huge variabilities due to the high number of processor resources shared amongst software functions.</para>
</section>
<section class="lev2" id="sec5-3-2">
<title>5.3.2 The Concept of Safety Margins</title>
<para>When testing an application and measuring its execution time, it is very likely, if not certain, that the (usually very rare) situation where the application takes its maximum execution time does not occur. This is due to either of the following reasons:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>The testing process failed to identify the set of input arguments that takes the longest execution path throughout the program&#x02019;s code, i.e., the path that leads to the WCET.</para></listitem>
<listitem>
<para>The testing process found the execution path(s) leading to the WCET but did not generate the maximal possible interference while exercising those paths. This means that the actual WCET is not observed only because the interference patterns generated during testing did not put the application into the worst execution conditions.</para></listitem>
</orderedlist>
<para>Regarding the first case, for most programs, the number of possible execution paths (in comparison to the high number of possible inputs) is too large to make exhaustive testing possible and/or realistic. Therefore, measurements are carried out only for a subset of input values. Typically, the testing process starts with the identification of a set of potentially &#x0201C;nasty&#x0201D; inputs that are likely to make the program take the longest execution path throughout its code and provoke its WCET. This step is typically supervised and based on some manual inspection of the code. Note that powerful tools exist such as the Rapita Verification Suite (RVS) that incorporates a code-coverage tool (RapiCover [16]) to test all parts of a given code and guarantee its full coverage during testing. We believe that such tools may be employed to help system designers identify the &#x0201C;worst&#x0201D; input datasets.</para>
<para>The problem of defining the worst input dataset(s) is thus not new, and to some extent it is independent of the underlying hardware architecture. Of course, the execution time of a given path depends on the execution time of each instruction in that path, and therefore is dependent on the architecture, but the method to search the space of all possible inputs and identify those that lead to the longest execution path is platform-agnostic. Since the problem was already there on single-core architectures, with mature solutions for it, we do not focus, in this book, on improving this part of the process.</para>
<para>Regarding the second point, it is always assumed that the worst-case interference is not observed during testing and therefore the maximum execution time recorded is an under-approximation of the actual WCET. To compensate for this, it is common to add a safety margin to the measured WCET, in the hope that the actual WCET lies below the resulting augmented estimation. The main question that remains open is whether the extra safety margin provably provides a safe bound, since it is based on some informed estimates. In principle, a very high margin yields an upper-bound on the execution time that is likely to be safe (i.e., greater than the actual WCET), but results in an over-dimensioned system with a low utilization of its resources, whereas a small margin may lead to an under-estimation of the actual system (worst-case) needs.</para>
<para>Traditionally, the magnitude of the safety margin applied to the maximum measured execution time is based on an estimation of the maximum interference (from the system or from other applications) that has not been observed during the testing phase but that the analyzed application could potentially incur at runtime. For single-core systems, this estimation of the worst-case interference is usually built on past experience. For example, in the IEC 61508 standard [17] related to functional safety of electrical/electronic/ programmable electronic safety-related systems, to ensure that the working capacity of the system is sufficient to meet the specified requirements, it is mentioned that:</para>
<blockquote>
<para><emphasis>&#x0201C;For simple systems an analytic solution may be sufficient, while for more complex systems some form of simulation may be more appropriate to obtain accurate results. Before detailed modeling, a simpler &#x02018;resource budget&#x02019; check can be used which sums the resources requirements of all the processes. If the requirements exceed designed system capacity, the design is infeasible. Even if the design passes this check, performance modeling may show that excessive delays and response times occur due to resource starvation. To avoid this situation, engineers often design systems to use some fraction (for example 50%) of the total resources so that the probability of resource starvation is reduced.&#x0201D;</emphasis></para>
</blockquote>
<para>As explained above, it is a common practice to simply add a margin of 50% (or any other percentage depending on the user&#x02019;s preferences and his level of confidence in those margins) to the maximum execution time observed. Unfortunately, on multicore and manycore architectures, experts are not yet able to safely estimate reliable margins, as there is no prior experience to be relied upon. Hence, we must build a new body of knowledge and investigate novel approaches to produce reliable timing estimates and margins, and we must motivate these estimations and justify why we believe they are reliable. Our move towards this ambitious goal is described in short in the following subsection.</para>
</section>
<section class="lev2" id="sec5-3-3">
<title>5.3.3 Our Proposed Timing Methodology at a Glance</title>
<para>In this book, we devised methods to extract both the intrinsic and extrinsic execution times. The overall timing analysis methodology consists of four steps:</para>
<para><emphasis role="underline">Step 1: Extraction of the maximum intrinsic execution time</emphasis></para>
<para>To measure the maximum intrinsic execution time (MIET), we run the analyzed task sequentially on one core and we configure the execution environment in such a way that no other tasks can interfere with its execution. That is, everything is done to nullify the interference with other applications or with the system itself. This way we put the analyzed task in &#x0201C;ideal&#x0201D; execution conditions in which, in the absence of interference, the time taken to execute its code can be assumed to be due solely to the execution of its instructions (without any stalling time). In these conditions, the task to be analyzed is run multiple times, non-preemptively, over a finite set of input data. These input data have been pre-selected and identified as particularly &#x0201C;nasty&#x0201D;, i.e., very likely to make the task take its longest execution path throughout its code and provoke its WCET. We do not elaborate on how to select those inputs.</para>
<para><emphasis role="underline">Step 2: Extraction of the maximum extrinsic execution time</emphasis></para>
<para>The maximum extrinsic execution time (MEET), on the contrary, is obtained by measuring the time taken to execute the analyzed task in conditions of &#x0201C;extreme&#x0201D; interference. That is, everything is done to maximize the interference with other applications and with the system itself. Measuring the execution time of the analyzed task in those &#x0201C;worst&#x0201D; conditions and over the &#x0201C;worst&#x0201D; input datasets give an estimation of the maximum execution time that the task may experience in the presence of other tasks running concurrently.</para>
<para><emphasis role="underline">Step 3: Extract the execution time after deployment</emphasis></para>
<para>The MIET and MEET can be considered as lower and upper bounds on the actual WCET of the analyzed task, since they estimate the WCET in conditions of no and extreme interference, respectively. These two estimations are useful to the system designers to understand the impact that tasks may have on each other&#x02019;s timing behavior. For instance, it may be desirable to derive a static mapping of the task-parts to the cores in which the task-parts (the portions of code for which the executions are timed or measured) that are highly sensitive to interference (i.e., the difference between their MEET and MIET is large) are mapped to specific cores in a way that they cannot interfere with each other at runtime.</para>
<para>After taking mapping and scheduling decisions based on the values of the MIET and MEET, these decisions are implemented and the whole system is run in its final configuration. Measures are taken again, this time to estimate the execution time of the tasks in its &#x0201C;final&#x0201D; execution environment, i.e., the environment corresponding to the &#x0201C;after-deployment&#x0201D;. Timed traces are recorded like in the previous step and are passed to step 4.</para>
<para><emphasis role="underline">Step 4: Estimate a worst-case execution time</emphasis></para>
<para>The traces collected in Step 3 reflect the actual execution time of every task-part, and from those their individual WCET can be derived or estimated. The simplest way to proceed is to retain the maximum execution time observed as the actual WCET. For safety purpose, an arbitrary extra &#x0201C;safety margin&#x0201D; can be added to that WCET estimation to make it even safer. The magnitude of the margin depends on how much &#x0201C;safer&#x0201D; the system designers want to be, but we would recommend using a margin that does not exceed the MEETs of the tasks (because the MEETs represent the WCET of the tasks in execution conditions that are unlikely to happen at runtime).</para>
<para>However, instead of arbitrarily choosing a margin, we advocate the use of statistical methods to analyze the traces and make a more &#x0201C;educated&#x0201D; choice driven by mathematical assumptions and computations rather than just a &#x0201C;gut feeling&#x0201D;. In this book, we use DiagXtrm, a complete framework to analyze timed traces and derive pWCET estimates.</para>
<para>In the next subsections, we describe every step of our methodology.</para>
</section>
<section class="lev2" id="sec5-3-4">
<title>5.3.4 Overview of the Application Structure</title>
<para>Before we go to the details, let us briefly recall the type of workloads that we are handling in this book and recap what exactly needs to be measured.</para>
<para>In the considered system model, the application comprises all the software parts of the systems that operate at the user-level and that have been explicitly defined by the user. The application is the software implementation (i.e., the code) of the functionality that the system must deliver to the end-user. It is organized as a collection of RT tasks.</para>
<para>An RT task is a recurrent activity that is a part of the overall system functionality to be delivered to the end-user. Every RT task is implemented and rendered parallelizable using OpenMP 4.5, which supports very sophisticated types of dynamic, fine-grained, and irregular parallelisms.</para>
<para>An RT task is characterized by a software procedure that must carry out a specific operation such as processing data, computing a specific value, sampling a sensor, etc. It is also characterized by a few (user-defined or computed) parameters related to its timing behavior such as its WCET, its period, and its deadline. Every RT task comprises a collection of task regions whose inter-dependencies are captured and modeled by a directed acyclic graph, or DAG.</para>
<para>A task region is defined at runtime by the syntactic boundaries of an OpenMP task construct. For example:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg132-1.jpg"/></para>
<para>Hence, hereafter we refer to task regions as <emphasis>OpenMP tasks</emphasis>. The OpenMP tasking and acceleration models are described in detail in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>.</para>
<fig id="F5-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F5-2">Figure <xref linkend="F5-2" remap="5.2"/></link></label>
<caption><para>Extended task dependency graph (eTDG) of an example application.</para></caption>
<graphic xlink:href="graphics/ch05_fig5_2.jpg"/>
</fig>
<para>An <emphasis>OpenMP task-part</emphasis> (or simply, a task-part) is a non-preemptible portion of an OpenMP task. Specifically, consecutive task scheduling points (TSP) such as the beginning/end of a task construct, the synchronization directives, etc., identify the boundaries of an OpenMP task-part. In the plain OpenMP task scheduler, a running OpenMP task can be suspended at each TSP (not between any two TSPs), and the thread previously running that OpenMP task can be re-scheduled to a different OpenMP task (subject to the task scheduling constraints).</para>
<para>The DAG of task regions can therefore be further expanded to form a typically bigger DAG of task-parts. This new graph of task-parts is called the extended task dependency graph (eTDG) of the RT task. <link linkend="F5-2">Figure <xref linkend="F5-2" remap="5.2"/></link> shows the eTDG of an example application. Our objective is to annotate every node, i.e., task-part, of the eTDG with an estimation of its WCET and then perform a schedulability analysis of the entire graph to verify that all the end-to-end timing requirements were met.</para>
</section>
<section class="lev2" id="sec5-3-5">
<title>5.3.5 Automatic Insertion and Removal of the Trace-points</title>
<para>In this subsection, we discuss how to respectively <emphasis>insert</emphasis> (Subsection 5.3.5.1) and <emphasis>remove</emphasis> (Subsection 5.3.5.2) trace-points in a given program in an automatic manner.</para>
<section class="lev2" id="sec5-3-5-1">
<title>5.3.5.1 How to insert the trace-points</title>
<para>To measure the execution time of a task-part, we insert a trace-point at its entry and exit points. A trace-point is a call to a system function that records the current timestamp. Therefore, the system will record the time of entering the task-part (i.e., when its execution starts) and the time at which it exits it; the difference between the two straightforwardly gives the time spent executing the task-part.</para>
<para>Inserting the trace-points into the tasks&#x02019; code can easily be done by the compiler itself, when creating the executable file. Moreover, upon compiling the code and creating the TDG, the compiler can assign a unique Identifier (ID) to every task-part. Overall, this ID can be used to define a trace-point for the task-part associated with an execution time. For example, using the trace system from the Kalray SDK, we ask the compiler to add the following trace-points at the beginning and end of every task-part as illustrated in the code snippet below:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg134-1.jpg"/></para>
<para>These trace-points indicate to the Kalray MPPA runtime environment that a time-stamp must be recorded each time the execution meets one of these points (together with the ID of the corresponding task-part). The first argument (here, &#x0201C;psocrates&#x0201D;) is the name of the &#x0201C;trace-point provider&#x0201D;. The user defines it to help him organize all its trace-points into groups. Informally, it can be thought of as a folder name. The second argument is the name of the trace-point. For every task-part we insert a trace-point called <literal>&#x02018;&#x02018;taskpartID__in&#x02019;&#x02019;</literal> at the beginning of the task-part and another trace-point called <literal>&#x02018;&#x02018;taskpartID__out&#x02019;&#x02019;</literal> at the end. We do so because the objective of our next tool is to find every matching pair <literal>&#x02018;&#x02018;<superscript>&#x02217;</superscript>__in/<superscript>&#x02217;</superscript>__out&#x02019;&#x02019;</literal> of trace-points and compute the difference of timestamps (which naturally corresponds to the execution time of the task-part).</para>
<para>Once all the trace-points are correctly placed into the source code, the compiler must create a separate header file &#x0201C;<emphasis>tracepoints.h</emphasis>&#x0201D; in which all the trace-points are declared and then include that file in all source files in which trace-points are used (#include &#x0201C;tracepoints.h&#x0201D;).</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg134-2.jpg"/></para>
<para>MPPA_DECLARE_TRACEPOINT(psocrates, taskpartID__in,(<literal>))</literal> MPPA_DECLARE_TRACEPOINT(psocrates, taskpartID__out, (<literal>))</literal></para>
<para>... // more trace-points</para>
<para>#endif</para>
</section>
<section class="lev2" id="sec5-3-5-2">
<title>5.3.5.2 How to remove the trace-points</title>
<para>After the analysis step, when the system is ready to be deployed, it is preferable to remove all the trace-points in order not to leave some &#x0201C;dead code.&#x0201D; A code is said to be dead either if it is never executed, or when its execution does not serve any purpose, like for example taking time-stamps and not recording them into a file (which would happen if those trace-points were to be left in the source code when compiling the application to be deployed). However, removing trace-points is not a benign operation.</para>
<para>To illustrate the problem that may arise from removing the trace-points, let us consider the following code.</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg135-1.jpg"/></para>
<para><literal>The user_main()</literal> function is a call to the main function of the benchmark program &#x0201C;statemate.c&#x0201D; provided by (15). If we disable all compiler optimizations during the compilation phase (this is important and will play a role later) and run this code 100 times on a single core of a compute cluster of the Kalray MPPA-256, we observe that the execution time oscillates consistently between 88492 and 88497 cycles (see <link linkend="F5-3">Figure <xref linkend="F5-3" remap="5.3"/></link>, left-hand side).</para>
<fig id="F5-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F5-3">Figure <xref linkend="F5-3" remap="5.3"/></link></label>
<caption><para>Impact of an unused variable on the execution time of an example application.</para></caption>
<graphic xlink:href="graphics/ch05_fig5_3.jpg"/>
</fig>
<para>Now, let us add to that code a variable <emphasis>x</emphasis> to which we assign an arbitrarily chosen integer (here, 1587) as shown below:</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg135-2.jpg"/></para>
<para>It is important to stress that the variable <emphasis>x</emphasis> is never used in the program. Since all compiler optimizations are disabled, the variable is not removed from the code by the compiler and is present in the assembly code that it produces. As seen in the <link linkend="F5-3">Figure <xref linkend="F5-3" remap="5.3"/></link> (right-hand side), the execution time now oscillates consistently between 88, 639 and 88, 636 cycles. This means that the addition of an unused variable to a part of the code which is not even under analysis adds around 140 cycles to the execution time of the measured portion of the code.</para>
<para>This increase in the execution time stems from the fact that after the addition of the line &#x0201C;int <emphasis>x</emphasis> = 1587&#x0201D; to the source code, all subsequent instructions got offset in the system memory by two times the length of an instruction, i.e., the line &#x0201C;int <emphasis>x</emphasis> = 1587&#x0201D; translates to two assembly instructions: one for allocating memory to the variable <emphasis>x</emphasis> and another one for moving the constant &#x0201C;1587&#x0201D; into it. Therefore, the portion of the code being timed has a different &#x0201C;memory layout&#x0201D; as it is mapped to the system memory two &#x0201C;instruction-lengths&#x0201D; further. This in turn impacts on the way the instructions of that part of the code are mapped at runtime to the instruction cache lines and ultimately it results in a perceptive difference in the execution time.</para>
<para>A consequence of this phenomenon is that removing the trace-points after the analysis phase may have for effect to substantially, or at least noticeably, alter the timing behavior of the application and all its task-parts. We came up with two potential solutions to this problem. The simpler one is to leave the trace-points in the code when compiling it for the final release of the application. Although it is a suitable work-around to the memory-shift problem described above, most designers are not in favor of having a dead portion of code, as explained above.</para>
<para>Our second solution is to measure the length, in number of assembly instructions, of the code being executed each time the function <literal>mppa_tracepoint</literal><literal>(</literal><literal>&#x02026;</literal> <literal>)</literal> is called and replace every such call with an equivalent number of NOPs (No Operation assembly instruction). This way neither the semantic of the code nor the memory layout are altered when removing the trace-points. We believe this solution to be both feasible and suitable for use in industrial applications.</para>
</section>
</section>
<section class="lev2" id="sec5-3-6">
<title>5.3.6 Extract the Intrinsic Execution Time: The Isolation Mode</title>
<para>In order to extract the MIET of a task-part, we must start its execution and make sure that it is isolated from the rest of the system. That is, we must nullify all external interference by turning off every other component that could potentially interfere with (and hence delay) the execution of the analyzed task-part. This is achieved by assigning every task-part of the analyzed real-time task to the same thread, and thus to the same core of the same cluster, and then making sure that all the other cores are kept idle. In other words, under this configuration, the RT task is executed sequentially in a single core. However, the intention of this phase is to analyze the execution time of each task-part in isolation, i.e., without suffering interferences, and not the overall RT task execution time. We call this configuration the <emphasis>isolation</emphasis> <emphasis>mode</emphasis>; the real-time task is then said to run <emphasis>in isolation</emphasis>.</para>
<para>To setup and enforce this isolation mode, we have implemented a platform-specific API. The current version has been written for the Kalray MPPA-256. The API provides a set of easy-to-use functions to configure the execution environment, as well as a set of global parameters and functions that are used to make sure that:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>all the openMP tasks are assigned to a single thread,</para></listitem>
<listitem>
<para>the IO cores and the cluster cores are in sync so that the environment is &#x0201C;sanitized&#x0201D; before and after the execution of every openMP task (nothing runs in the background that could interfere with the execution of the analyzed task), and</para></listitem>
<listitem>
<para>additional functions allow the user to perform specific operations, either before the runtime, such as deciding the memory-mapping and cache-management policy, or during the runtime, such as invalidating the instruction or data caches before executing each task-part.</para></listitem>
</orderedlist>
<para>The main objective of the API is to create a controlled environment in which every task-part is run over a specific set of inputs and is isolated from the rest of the system so that it incurs minimum interference during its execution.</para>
</section>
<section class="lev2" id="sec5-3-7">
<title>5.3.7 Extract the Extrinsic Execution Time: The Contention Mode</title>
<para>To extract the MEET of a task-part, we start the task and interfere as much as possible with its execution at runtime. The objective of the contention mode is to create the &#x0201C;worst&#x0201D; execution conditions for the task-parts so that their execution is constantly suspended due to interference with other tasks. In this step, for each task-part, we record the maximum execution time observed under those conditions. This gives us an estimation of the maximum execution time of each task-part when it suffers interference from other tasks on the shared resources.</para>
<para>This <emphasis>contention mode</emphasis> is similar to the isolation mode in that all the task-parts of the analyzed real-time task are assigned to the same thread, and thus to the same core within a same cluster, effectively executing the RT task sequentially. However, contrary to the isolation mode that shuts down all the other cores of the cluster (thereby nullifying all possible interference within that cluster), we deploy onto all these other cores small programs called IG, which stands for <emphasis>Interference Generator</emphasis>. Those programs are essentially tiny pieces of code that have the sole purpose of saturating all the resources (e.g., interconnection, memory banks) that are shared with the task-parts under analysis. Recall that the objective of the contention mode is to create the worst execution conditions for the execution of the task-parts, conditions in which their execution is slowed down as much as possible due to contention for shared resources.</para>
<para>Implementing the IG that generates the worst possible interference that a task-part could ever suffer is a very challenging, if not impossible, task. This is because the exact behavior of the task-part to be interfered with (i.e., its utilization pattern of every shared resources and the exact time-instants of accessing it) should be known, as well as all the detailed specifications of the platform. Besides, even if that information was known, the execution scenario causing the maximum interference may be impossible to reproduce. Rather than concentrating our efforts on creating such a &#x0201C;worst IG&#x0201D;, we have opted for the implementation of an IG that is &#x0201C;bad enough&#x0201D; and used it as a proof of concept to demonstrate how large the time-overhead incurred by the task-parts due to the interference can be.</para>
<para>Our implementation of the IG consists of a single function <emphasis>IG_main</emphasis> that is executed by a thread dispatched to every core on which the task-parts are not assigned (recall that the application under analysis is executed sequentially in a single core). That is, every core that is not running the task-parts runs a thread that executes <emphasis>IG_main</emphasis>. Essentially, <emphasis>IG_main</emphasis> executes three functions, namely:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>IG_init_inteference_process ()</para></listitem>
<listitem>
<para>IG_generate_interference ()</para></listitem>
<listitem>
<para>IG_exit_inteference_process ()</para></listitem>
</orderedlist>
<para>The first one is called upon deploying the IG, at the beginning of <emphasis>IG_main</emphasis>, before the task-parts start to execute and be timed. The second one is the main function. It creates interference on the shared resources. The call to that function is encapsulated in a loop that terminates only when the <emphasis>IG_main</emphasis> is explicitly told to stop. Finally, the third function is called when all the task-parts have been timed and the analysis process is about to end.</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg139-1.jpg"/></para>
<para>Let us now briefly describe our implementation of the IG on the Kalray MPPA-256. This implementation is provided in a single file, which starts with the declaration of an array of integer called <literal>my_array</literal> and declares the three main functions as described above. The <literal>__attribute__<literal>((</literal>always_inline<literal>))</literal></literal> instruction is used to enforce and oblige the compiler to use inlining for these three methods. The inlining technique is used to waste as little time as possible jumping from one address to another in the code, as jumping does not create interference.</para>
<para>Below is a code snippet of the first function &#x0201C;<emphasis>IG_init_interference_ process().</emphasis>&#x0201D;</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg139-2.jpg"/></para>
<para>This function simply allocates memory to <literal>my_array</literal> (1024 integers) and fills that memory space with arbitrary values. Note that on the Kalray MPPA-256, a thousand integers occupy roughly half of the private data cache of a VLIW <footnote id="fn5_2" label="2"> <para>Very Long Instruction Word.</para></footnote> core in a compute cluster.</para>
<para>The third function, &#x0201C;<emphasis>IG_exit_inteference_process()&#x0201D;,</emphasis> is the simplest as it only frees the memory space held by <literal>my_array</literal> as shown below.</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg139-3.jpg"/></para>
<para>The second function, &#x0201C;<emphasis>IG_generate_interference ()</emphasis>,&#x0201D; is the main one and a snippet of its code is presented below.</para>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg140-1.jpg"/></para>
<para>The function starts by invalidating the content of the data and instruction caches. Then, it reads every element of &#x0201C;<emphasis>my_array</emphasis>&#x0201D;, starting from the element K = 0 and moving on iteratively from element K to element ((K+8) mod 1024), until K reaches 1023. This way, every element of the array is read exactly once and every two consecutive readings access data that are located exactly 8 &#x0002A; 4 = 32 bytes apart in the memory (the size of an integer is standard on the Kalray, i.e., 4 bytes). This is done on purpose knowing that the private data cache line of every VLIW core in the compute clusters of the Kalray MPPA-256 is 32 bytes long. Consequently, every reading causes a cache miss and the value must then be fetched from the 2 MB in-cluster shared memory, hence it creates traffic on the shared memory communication channels and potentially interferes with the task-part being analyzed.</para>
<para>By running the task-parts concurrently with these IGs, every request sent by a task-part to read or write a data in the shared memory is very likely to interfere with a read request from one of the IGs. We have conducted experiments on the Kalray MPPA-256 using several use-case applications to evaluate the magnitude of the increase in the execution time due to this interference. Depending on the configuration of the board and the memory footprint of the task-parts and their communication pattern with the memory, the difference between the maximum execution time observed in isolation mode and in contention mode is substantial as the execution time of a task-part may be increased by a factor of 9.</para>
</section>
<section class="lev2" id="sec5-3-8">
<title>5.3.8 Extract the Execution Time in Real Situation: The Deployment Mode</title>
<para>After determining the intrinsic and extrinsic execution times (i.e., the MIET and the MEET), we communicate them to the mapping and scheduling analysis tools through the annotation of the TDG of the real-time task. Once all necessary mapping and scheduling decisions are taken, the application is run again, but this time in its final production environment. This means that the platform configuration and mapping and scheduling decisions are no longer imposed and defined so as to create specific execution conditions. Then, we collect runtime-timed traces of the task-parts in their final environment, without any supervision or any attempt to explicitly favor or curb the execution of the application.</para>
</section>
<section class="lev2" id="sec5-3-9">
<title>5.3.9 Derive WCET Estimates</title>
<para>As already discussed, the traces collected in the previous step reflect the actual execution time of every task-part when they run in their final environment, under different execution conditions. The objective of this final step is to derive WCET estimates from those traces. The simplest solution is to retain the maximum execution time observed during the deployment mode as the actual WCET and, for safety purposes, add an arbitrary &#x0201C;safety margin&#x0201D; to that maximum to make it &#x0201C;safer&#x0201D;. The magnitude of the margin depends on how much &#x0201C;safer&#x0201D; the system designers want to be, but we would recommend using a margin that does not exceed the MEET. However, instead of arbitrarily choosing a margin, we advocate the use of statistical methods to analyze the traces and make a better thought out choice.</para>
<para>The objective of Measurement-Based Probabilistic Timing Analysis (MBPTA) approaches is to characterize the variability in the execution time of a program through probability distributions and in particular, they aim at deriving probabilistic WCET estimates, a.k.a. pWCET. A pWCET is a probability distribution of the WCET of a program. That is, through MBPTA, the WCET is no longer expressed as a single value but as a range of values, each assigned to a given probability of occurrence with the obvious relation: the higher the value assumed to be the WCET, the lower its probability of occurrence. Based on this framework system designers are in a position to somewhat decide on the reliability of the final WCET estimation, simply by ignoring all values for which the probability of observing an execution time greater than those exceeds a pre-decided threshold. The EVT is a popular theoretical tool used by most MBPTA approaches. The EVT aims at modeling and estimating better the tail of a statistical distribution, which is <emphasis>de facto</emphasis> what the MBPTA is trying to achieve when focusing on the pWCET.</para>
<para>Researchers at the French Aerospace Lab (ONERA), in France, recently proposed a remarkable framework and tool to analyze timed traces and derive pWCET estimates. The framework is called DiagXtrm [18] and defines a methodology composed of three main steps:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Analyze the traces</para></listitem>
<listitem>
<para>Derive pWCET estimates using the EVT</para></listitem>
<listitem>
<para>Assess the quality of the estimations.</para></listitem>
</orderedlist>
<para>Together with the theory and the definition of the methodology, they developed a tool to diagnose execution time traces and derive safe pWCET estimates using the EVT. However, the EVT can be applied to a given trace only if some hypotheses are verified. Testing those hypotheses is the focus of the first step (&#x0201C;Analysis of the traces&#x0201D;) above.</para>
<para>In a nutshell, for safely applying the EVT and getting reliable pWCET estimates, one has to check a few hypotheses including for instance stationarity, short-range dependence, and extreme independence. The <emphasis>stationarity</emphasis> of a trace reveals whether measurements belong to the same probabilistic law without knowing it. The <emphasis>independence</emphasis> (short-ranged or between the extremes) analysis aims at determining whether there are obvious correlations within the measurements. Systemic effects in a modern hardware platform are so complex and numerous that it is quite impossible to infer the probability of happening of an execution time knowing the value of the preceding ones, i.e., the execution time of an application cannot be inferred from the execution times of its previous executions. System non-determinism, coming from the considered system&#x02019;s degree of abstraction, knowledge, and randomness observed in a timed trace motivate the independence of the measurements that has to be studied at &#x0201C;different scales&#x0201D; (i.e., short-range independences and independences of the extremes). DiagXtrm implements the most advanced tests to verify the stationarity hypothesis and measure the degree of correlation between patterns of different lengths within a trace. Thus, it studies both short-range and distant dependencies between the measurements.</para>
<para>If all the hypotheses are verified, then the EVT is applied to produce pWCET estimates. These estimates are the result of sophisticated computations based on parameters that must be carefully set. The user is in charge of setting those parameters as he wants, and thus has a great influence on the pWCET estimation process. Note however that the DiagXtrm tool provides helpful functions to guide the choice of many of those input parameters. Finally, the tool features a set of tests to evaluate the quality of the produced estimates, together with other tests to assess the confidence that all the hypotheses were verified. We believe that this last phase is fundamental and is a first step towards building confidence and assessing the reliability of the pWCET estimates.</para>
</section>
</section>
<section class="lev1" id="sec5-4">
<title>5.4 Summary</title>
<para>The analysis of the timing behavior of software applications that expose real-time requirements and dedicated to execute on the recent COTS manycore platforms such as the Kalray MPPA-256 raises a number of important issues. Because a reliable and tight WCET estimation for each task running on such a platform is a crucial input at the schedulability analysis level, we showed that it is neither acceptable nor realistic to ignore all the interactions between each analyzed task, the OS, and all the other tasks running in the system. Then, depending of the type of workload that is considered, we also showed that the choice of the methodology to be adopted must be conducted with care. In this chapter, after presenting an overview of all the possible methodologies, and after discussing their advantages and disadvantages, we opted for a measurement-based approach. We explained and motivated this choice and finally presented the details of our solution. Here, we showed that both the intrinsic (MIET) and extrinsic (MEET) execution times of each task are pivotal values to be extracted in order to guide the designer in deriving a reliable and tight WCET.</para>
</section>
<section class="lev1" id="sec5-5">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis>OTAWA</emphasis>. Available at: <ulink url="http://www.irit.fr/recherches/ARCHI/MARCH/OTAWA/doku.php?id=doc:computing_a_wcet">http://www.irit.fr/recherches/ARCHI/MARCH/OTAWA/doku.php?id=doc:computing_a_wcet</ulink>.</para></listitem>
<listitem>
<para>Ermedahl, A., Engblom, J., &#x0201C;Execution Time Analysis for Embedded Real-Time Systems,&#x0201D; eds. Joseph, Y-T., Leung, S. H., Son, I. L., Chapman and Hall/CRC &#x02013; Taylor and Francis Group, 2007.</para></listitem>
<listitem>
<para>Lokuciejewski, P., Marwedel, P., <emphasis>Worst-Case Execution Time Aware Compilation Techniques for Real-Time Systems &#x02013; Summary and Future Work</emphasis> (Springer: Netherlands), pp. 229&#x02013;234, 2011. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lokuciejewski%2C+P%2E%2C+Marwedel%2C+P%2E%2C+Worst-Case+Execution+Time+Aware+Compilation+Techniques+for+Real-Time+Systems+-+Summary+and+Future+Work+%28Springer%3A+Netherlands%29%2C+pp%2E+229-234%2C+2011%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>AbsInt GmbH</emphasis>. Available at: <ulink url="http://www.absint.com/ait/analysis.htm">http://www.absint.com/ait/analysis.htm</ulink>.</para></listitem>
<listitem>
<para><emphasis>Tidorum Ltd</emphasis>. Available at: <ulink url="http://www.bound-t.com/">http://www.bound-t.com/</ulink>.</para></listitem>
<listitem>
<para><emphasis>NUS</emphasis>. Available at: <ulink url="http://www.comp.nus.edu.sg/&#x0223C;rpembed/chronos/">http://www.comp.nus.edu.sg/&#x0223C;rpembed/chronos/</ulink>.</para></listitem>
<listitem>
<para><emphasis>IRISA</emphasis>. Available at: <ulink url="http://www.irisa.fr/alf/index.php?option=com_content&#x00026;view=article&#x00026;id=29&#x00026;Itemid=&#x00026;lang=fr">http://www.irisa.fr/alf/index.php?option=com_content&#x00026;view=article&#x00026;id=29&#x00026;Itemid=&#x00026;lang=fr</ulink>.</para></listitem>
<listitem>
<para><emphasis>MRTC</emphasis>. Available at: <ulink url="http://www.mrtc.mdh.se/projects/wcet/sweet/DocBook/out/webhelp/index_frames.html">http://www.mrtc.mdh.se/projects/wcet/sweet/DocBook/out/webhelp/index_frames.html</ulink>.</para></listitem>
<listitem>
<para>Kirner, R., Puschner, P., Wenzel, I., &#x0201C;Measurement-based worst-case execution time analysis using automatic test-data generation.&#x0201D; <emphasis>4th Euromicro International Workshop on WCET Analysis</emphasis>, pp. 67&#x02013;70, 2004. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Kirner%2C+R%2E%2C+Puschner%2C+P%2E%2C+Wenzel%2C+I%2E%2C+%22Measurement-based+worst-case+execution+time+analysis+using+automatic+test-data+generation%2E%22+4th+Euromicro+International+Workshop+on+WCET+Analysis%2C+pp%2E+67-70%2C+2004%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>Rapita Systems Ltd</emphasis>. Available at: <ulink url="http://www.rapitasystems.com/products/rapitime/how-does-rapitime-work">http://www.rapitasystems.com/products/rapitime/how-does-rapitime-work</ulink>.</para></listitem>
<listitem>
<para>Carnevali, L., Melani, A., Santinelli, L., Lipari, G., &#x0201C;Probabilistic Deadline Miss Analysis of Real-Time Systems Using Regenerative Transient Analysis.&#x0201D; In <emphasis>Proceedings of the 22nd International Conference on Real-Time Networks and Systems</emphasis>, Versaille, pp. 299&#x02013;308, 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Carnevali%2C+L%2E%2C+Melani%2C+A%2E%2C+Santinelli%2C+L%2E%2C+Lipari%2C+G%2E%2C+%22Probabilistic+Deadline+Miss+Analysis+of+Real-Time+Systems+Using+Regenerative+Transient+Analysis%2E%22+In+Proceedings+of+the+22nd+International+Conference+on+Real-Time+Networks+and+Systems%2C+Versaille%2C+pp%2E+299-308%2C+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Santinelli, L., Morio, J., Dufour, G., Jacquemart, D., &#x0201C;On the Sustainability of the Extreme Value Theory for WCET Estimation.&#x0201D; 14th International Workshop on Worst-Case Execution Time Analysis, Versailles, pp. 21&#x02013;30, 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Santinelli%2C+L%2E%2C+Morio%2C+J%2E%2C+Dufour%2C+G%2E%2C+Jacquemart%2C+D%2E%2C+%22On+the+Sustainability+of+the+Extreme+Value+Theory+for+WCET+Estimation%2E%22+14th+International+Workshop+on+Worst-Case+Execution+Time+Analysis%2C+Versailles%2C+pp%2E+21-30%2C+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>Proartis: Probabilistically Analysable Real-Time Systems</emphasis>. Available at: <ulink url="http://www.proartis-project.eu/">http://www.proartis-project.eu/</ulink>. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Proartis%3A+Probabilistically+Analysable+Real-Time+Systems%2E+Available+at%3A+http%3A%2F%2Fwww%2Eproartis-project%2Eeu%2F%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>Probabilistic real-time control of mixed-criticality multicore and manycore systems (PROXIMA)</emphasis>. Available at: <ulink url="http://www.proxima-project.eu/">http://www.proxima-project.eu/</ulink>.</para></listitem>
<listitem>
<para><emphasis>Rapita Systems Ltd</emphasis>. Available at: <ulink url="https://www.rapitasystems.com/products/rapicover">https://www.rapitasystems.com/products/rapicover</ulink>.</para></listitem>
<listitem>
<para><emphasis>The International Electrotechnical Commission. Functional Safety of Electrical/Electronic/Programmable Electronic Safety-Related Systems &#x02013; Part 7, 2nd Edition, Requirement C.5.20 (Performance Modeling)</emphasis>, Geneva, p. 99, 2010. IEC 61508. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=The+International+Electrotechnical+Commission%2E+Functional+Safety+of+Electrical%2FElectronic%2FProgrammable+Electronic+Safety-Related+Systems+-+Part+7%2C+2nd+Edition%2C+Requirement+C%2E5%2E20+%28Performance+Modeling%29%2C+Geneva%2C+p%2E+99%2C+2010%2E+IEC+61508%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Gustafsson, J., Betts, A., Ermedahl, A., Lisper, B., &#x0201C;The M&#x000E4;lardalen WCET benchmarks &#x02013; past, present and future.&#x0201D; In <emphasis>Proceedings of the 10th International Workshop on Worst-Case Execution Time Analysis (WCET&#x02019;2010)</emphasis> Brussels, Belgium, pp. 137&#x02013;147, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Gustafsson%2C+J%2E%2C+Betts%2C+A%2E%2C+Ermedahl%2C+A%2E%2C+Lisper%2C+B%2E%2C+%22The+M%E4lardalen+WCET+benchmarks+-+past%2C+present+and+future%2E%22+In+Proceedings+of+the+10th+International+Workshop+on+Worst-Case+Execution+Time+Analysis+%28WCET%272010%29+Brussels%2C+Belgium%2C+pp%2E+137-147%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Onera. <emphasis>Onera &#x02013; DiagXTrm</emphasis>, Available at: <ulink url="https://forge.onera.fr/projects/diagxtrm2">https://forge.onera.fr/projects/diagxtrm2</ulink>.</para></listitem>
<listitem>
<para>MTime, <emphasis>Vienna real-time systems group</emphasis>, Available at: <ulink url="http://www.vmars.tuwientuwien.ac.at">http://www.vmars.tuwientuwien.ac.at</ulink>.</para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch06" label="6" xreflabel="6">
<title>OpenMP Runtime</title>
<para><emphasis role="strong">Andrea Marongiu<superscript>1,2</superscript>, Giuseppe Tagliavini<superscript>2</superscript> and EduardoQui&#x000F1;ones<superscript>3</superscript></emphasis></para>
<para><superscript>1</superscript>Swiss Federal Institute of Technology in Z&#x000FC;rich (ETHZ), Switzerland<?lb?><superscript>2</superscript>University of Bologna, Italy<?lb?><superscript>3</superscript>Barcelona Supercomputing Center, Spain</para>
<para>This chapter introduces the design of the OpenMP runtime and its key components, the <emphasis>offoading library</emphasis> and the <emphasis>tasking runtime library</emphasis>. Starting from the execution model introduced in the previous chapters, we first abstractly describe the main interactions among the main actors involved in program execution. Then we focus on the optimized design of the offoading library and the tasking runtime library, followed by their performance characterization.</para>
<section class="lev1" id="sec6-1">
<title>6.1 Introduction</title>
<para>The model assumed in the previous chapters considers the existence of multiple applications, starting execution on the <emphasis>host</emphasis> processor, and each one is composed of multiple real-time (RT) tasks which can be sent to the <emphasis>accelerator</emphasis> with the aim to speed up their execution. This paradigm, commonly referred to as <emphasis>offoading</emphasis>, has been widely adopted in many computing domains from embedded systems to HPC [1, 2]. In the context of the Kalray architecture (described in <link linkend="ch02">Chapter <xref linkend="ch2" remap="2"/></link>), IO cores take on the <emphasis>host</emphasis> role while the <emphasis>clusters</emphasis> are used as accelerators. Accordingly, an OpenMP-based software stack with offoading support must leverage both host and acceleration roles. On the <emphasis>host</emphasis> side, an OpenMP directive (<literal>#pragma omp target</literal>) is used to specify a region of code which can be offoaded. Inside a cluster, a pool of threads is dedicated for the execution of the offoaded workload and the RTOS (introduced in <link linkend="ch07">Chapter <xref linkend="ch7" remap="7"/></link>) is in charge of scheduling the execution of the threads on the available cores.</para>
<para>The complete software stack to handle the described execution model is composed of an <emphasis>offoading library</emphasis> and a <emphasis>tasking runtime library</emphasis>. The offoading library executes on the <emphasis>host</emphasis> and is in charge of initiating offoad sequences to the accelerator. On the accelerator side, the <emphasis>request manager</emphasis> (RM) is the component in charge to collect offoad requests and create pools of threads (hereafter called <emphasis>jobs</emphasis> as in the OS terminology) to execute them. Depending on runtime design and hardware specific features, the RM can be implemented as an RT task (a software component) or may be mapped to a dedicated core (a hardware component). The tasking runtime library provides an optimized support for task parallelism on the accelerator and runs on top of the RTOS. It is further divided into a low-level library (or LL-RTE) [3], where all the tightly coupled interactions with the RTOS are implemented, plus a high-level library, where all the management of the tasking constructs resides.</para>
</section>
<section class="lev1" id="sec6-2">
<title>6.2 Offoading Library Design</title>
<para><link linkend="F6-1">Figure <xref linkend="F6-1" remap="6.1"/></link> summarizes the timing diagram (time flows from top to bottom on the vertical axis) and the interactions between the software blocks providing the offoad support. At the higher level of abstraction, the <emphasis>host</emphasis> sends request to the RM, which orchestrates the execution of the workload on the <emphasis>processing elements</emphasis> (PEs).</para>
<fig id="F6-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-1">Figure <xref linkend="F6-1" remap="6.1"/></link></label>
<caption><para>Timing diagram of an offoading procedure.</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_1.jpg"/>
</fig>
<para>The <emphasis>host</emphasis> support is implemented as a user-level library that interfaces OpenMP offoads (expressed at the application level with a <literal>target</literal> directive) to the computing clusters. The key features of this library can be summarized as follows:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis role="strong">Low-cost offoad:</emphasis> As initializing the communication channels between the <emphasis>host</emphasis> and the offoad manager and loading into the cluster shared memory the binary file containing the OpenMP library (high-level library + LL-RTE) are costly operations, the <emphasis>host</emphasis> offoad library implements it as a one-time operation that happens at system startup (the <literal>GOMP_init</literal> method). Every time the <emphasis>host</emphasis> program encounters a <literal>target</literal> directive, this is translated into a call to the <literal>GOMP_target</literal> function, which sends a control packet to the offoad manager of the target cluster and then triggers the copy of input data. This handshake procedure is streamlined to guarantee minimum overhead.</para></listitem>
<listitem>
<para><emphasis role="strong">Asynchronous offoad:</emphasis> The offoad procedure is asynchronous. After sending the offoad request to the cluster, <literal>GOMP_target</literal> immediately returns to the caller (with the exception of the multi-offoad case described below). The result of the offoad computation can be retrieved by calling a blocking synchronization primitive (<literal>GOMP_target_wait</literal>).</para></listitem>
<listitem>
<para><emphasis role="strong">Multi-cluster support:</emphasis> An application can perform offoads on different clusters, from 1 up to 16. The initialization is required for each cluster that is used by the current application. The cluster is specified by the programmer using the OpenMP syntax (i.e., the <literal>device</literal> clause of a <literal>target</literal> directive).</para></listitem>
<listitem>
<para><emphasis role="strong">Multi-offoad support:</emphasis> An application can perform multiple offoads on the same cluster. At the same time, multiple offoads can coexist on the same cluster at different priority levels <footnote id="fn6_1" label="1"> <para>Focusing on the MPPA-256 platform, currently two levels are supported on RTEMS hosts and four on Linux hosts</para></footnote>. The priority level is specified by the programmer using the OpenMP syntax (i.e., the <literal>priority</literal> clause), and it is propagated to the runtime using a parameter of <literal>GOMP_target</literal>.</para></listitem>
</itemizedlist>
<para>The function calls to the offoading runtime are not invoked directly by the developer, as the OpenMP syntax is used to identify the code and data to be offoaded. The compiler transforms the offoading OpenMP directives as defined in its accelerator model (i.e., <literal>target</literal> and <literal>declare target</literal> directives) to the corresponding offoading runtime calls, as described in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>.</para>
<para>In our design, the RM is implemented as a persistent RTOS task to be executed on the accelerator side (i.e., by one of the cluster cores). The RTOS leverages the notion of a <emphasis>task scheduling point</emphasis> (TSP) to check the availability of a new offoad request and perform the requested actions. At each such scheduling point, the RTOS can (re)start the execution of the RM itself (if a new offoad has arrived in the meantime and needs to be enqueued to the ready job list) or another job in the queue, depending on the synchronization policy adopted. TSPs are naturally identified as synchronization points in an OpenMP program (see <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link> for more details). The OS provides synchronization primitives (described in <link linkend="ch07">Chapter <xref linkend="ch7" remap="7"/></link>) which can be used to block one (or more) thread(s) within a job on a certain wait condition, and the OpenMP runtime invokes these primitives to enforce synchronization.</para>
<para>To reduce the runtime overheads, the metadata for all the supported RTOS jobs on a cluster (one per priority level) are created and initialized upon the first call to the RM. The activated jobs execute the <literal>GOMP_main</literal> function of the runtime library to initialize the offoad support on the cluster side.</para>
</section>
<section class="lev1" id="sec6-3">
<title>6.3 Tasking Runtime</title>
<para>The OpenMP tasking model has been introduced in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>. Task-based parallelism offers a powerful conceptual framework to exploit irregular parallelism in target applications, and several works have demonstrated the effectiveness of tasking [4&#x02013;7]. However, the sophisticated semantics of the OpenMP tasking execution model are translated into a complex control code that has to be executed in addition to the application code itself. This ultimately results in significant time overheads, if the application tasks are not large enough to hide such overheads. Thus, a performance-efficient design of a tasking runtime environment (RTE) targeting low-end embedded manycore accelerators is a challenging task, as embedded parallel applications typically exhibit very fine-grained parallelism [6, 8], and are thus very sensitive to time overheads. Moreover, memory overheads are also very relevant in this context, as embedded architectures feature very limited amounts of fast, on-chip memory. Allocating runtime support metadata in such memories reduces time overheads, as the control code executes faster, but reduces the space available for program data. As the metadata for a tasking runtime might consume a significant amount of memory, it is necessary to find a good tradeoff between the implied space and time overheads.</para>
<para>The applicability of the tasking approach to embedded applications and embedded manycore accelerators is often limited to coarse-grained parallel tasks, capable of tolerating the high overheads typically implied in a tasking runtime. State-of-the-art tasking runtimes for embedded manycores [6] succeed in achieving low overheads and enabling high speedups for very fine-grained tasks, but only for simple <emphasis>flat</emphasis> parallel patterns (i.e., where all the tasks are created from the same parent task). The main reason for this limitation lies in a key design choice: only <emphasis>tied</emphasis> tasks are supported by most RTEs, whereas <emphasis>untied</emphasis> tasks are not supported. If a <emphasis>tied</emphasis> task is suspended (due to synchronization, creation of another task, etc.), only the thread that initially owned it is allowed to resume its execution. This clearly significantly limits the available parallelism when more sophisticated (and realistic) parallel execution patterns are considered, like nested tasking (for instance, in programs that use recursion).</para>
<para><emphasis role="strong">Scheduling policies:</emphasis> Another limitation that follows from supporting only <emphasis>tied</emphasis> tasks is the restricted set of scheduling policies available. Breadth-first scheduling (BFS) and work-first scheduling (WFS) are the two most widely used policies for distributing tasks among available threads. Upon encountering a task creation point: (i) BFS will push the new task in a queue and continue execution of the parent task and (ii) WFS will suspend the parent task and start execution of the new task. BFS tends to be more demanding in terms of memory, as it creates all tasks before starting their execution (and thus all tasks coexist simultaneously). This is an undesirable property in general and in particular for resource-constrained embedded systems, which would make WFS a better candidate. WFS also has the nice property of following the execution path of the original sequential program, which tends to result in better data locality [5]. However, when <emphasis>tied</emphasis> tasks are used, BFS is the only choice in practice, as WFS leads to a complete serialization of task executions when nested parallelism is adopted. Moreover, it has been shown that the use of <emphasis>untied</emphasis> tasks significantly reduces the worst case response time analysis [9].</para>
<para><emphasis role="strong">Task queue:</emphasis> The most widespread design solution to support the OpenMP tasking execution model is to rely on a centralized task queue. This minimizes memory footprint for runtime support metadata, which is a must in the context of embedded platforms. The basic building block of the proposed design focuses on lightweight support for <emphasis>push</emphasis> and <emphasis>pop</emphasis> operations on such a centralized queue (upon task creation and extraction, respectively), relying on fine-grained locking mechanisms. TSPs are implemented using lightweight events, which avoids the massive contention implied by active polling (idle threads on the TSP are put into sleep mode). When a task is created (i.e., pushed in the queue), the creator thread sends a signal which wakes up a single thread (selected using round-robin). After completing the task execution, the thread returns into sleeping mode. The described queue is implemented with a doubly linked list. This data structure allows to <emphasis>push</emphasis> and <emphasis>pop</emphasis> tasks from the queue and also remove a task in any position of the queue. This is key for low overhead, as tasks are not constrained to execute in-order (except when dependencies are specified), so their completion and removal from the queue is independent of their position. Note that a simple linked list does not allow this operation.</para>
<para><emphasis role="strong">Untied tasks:</emphasis> The described support is sufficient to show excellent performance in the presence of simple flat parallel patterns, where all the tasks are created from within a single level (i.e., a single parent task), but lacks the capability of supporting more sophisticated forms of parallelism, like nested parallel patterns found in programs that use recursion, and for which the tasking model was originally proposed. Consequently, <emphasis>untied</emphasis> tasks are not supported by using this basic implementation. Due to the limitations of <emphasis>tied</emphasis> tasks described previously, the scheduling policy relies on BFS, and WFS is not supported. In the following, we describe how we extend this baseline implementation to fully support nested parallel patterns and <emphasis>untied</emphasis> tasks, while keeping the implementation lightweight and not too memory-hungry. These both are the key requirements for any implementation suitable for embedded manycore accelerators. Our main goal is to achieve a comparable efficiency in terms of task granularity (the finer the better) for which near-ideal speedups are achieved.</para>
<fig id="F6-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-2">Figure <xref linkend="F6-2" remap="6.2"/></link></label>
<caption><para>Task suspension in the baseline implementation (considering tied tasks and WFS).</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_2.jpg"/>
</fig>
<para><link linkend="F6-2">Figure <xref linkend="F6-2" remap="6.2"/></link> shows how task suspension works in most implementations supporting <emphasis>tied</emphasis> tasks (WFS is assumed). The thread on which the code shown in the figure is executing has an associated stack (depicted on the left). When a <literal>task</literal> directive is encountered, the thread jumps to a runtime function that manages the creation of a new task from the enclosed code region. Because WFS is considered, the thread encountering the new task executes the code encapsulated within the task region, and the parent task is suspended (as it is a tied task and so cannot migrate to a different thread). A new stack frame is activated for this task, like in every regular function call. The same thing happens at every nested <literal>task</literal> directive. When a task is completed, the stack pointer is reset to the top of the previous active frame. Since the semantics of <emphasis>tied</emphasis> task scheduling ensure that suspension/resumption can happen only on the same thread, no explicit bookkeeping to save/restore the context of a task is required.</para>
<para>The key extension required to support <emphasis>untied</emphasis> tasks is the capability of allowing to resume a suspended task on a different thread than the one that started and suspended it. To achieve this goal, we rely on lightweight <emphasis>co-routines</emphasis> [10]. Co-routines rely on cooperative tasks that publicly expose their code and memory state (register file, stack), so that different threads can take control of the execution after restoring the memory state. Every time that a thread suspends or resumes a suspended cooperative task, a context switch is performed. We place the required metadata to support task contexts (TCs) in the shared multi-bank memory and we use inline assembly to minimize the cost of the routines to save and restore the architectural state.</para>
<fig id="F6-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-3">Figure <xref linkend="F6-3" remap="6.3"/></link></label>
<caption><para>Untied task suspension with task contexts and per-task stacks.</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_3.jpg"/>
</fig>
<para><link linkend="F6-3">Figure <xref linkend="F6-3" remap="6.3"/></link> shows how task suspension works in our approach for <emphasis>untied</emphasis> tasks (WFS is assumed). Initially, the thread on which the code shown in the figure is executing uses its own private stack (in gray). When the outermost task region (T0) is encountered, the context of the current task is saved in the TC (including the current SP, that is, the task pointer register), then the thread is rescheduled to execute the new task T0. The SP of the thread is updated to the stack of T0 (in blue) and the new task is started. When the creation point of the innermost task T1 is reached, an identical procedure is followed. The context of T0 is saved in its TC, which is pushed back in the queue, then thread 0 is pointed to the stack of T1 (in red). Now the suspended T0 can be pulled out of and restarted by thread 1. On top of this basic mechanism, a number of other design choices were made to minimize the cost of our runtime support, which we describe in the following.</para>
<para><emphasis role="strong">Task hierarchy:</emphasis> Supporting nested tasks requires to keep in the runtime a data structure (a <emphasis>tree</emphasis>) that represents the hierarchy of multiple task regions. A parent task has a link to its children and vice versa, to facilitate exchange of information about execution status. For example, a parent task needs to be informed about the execution completion of its children to support the semantics of the <literal>taskwait</literal> directive. When a parent task completes its execution, its children become orphans and should not care to inform the parent. The fastest solution to handle parent task termination in terms of bookkeeping would be not to delete the descriptor, but just to maintain the task in a <emphasis>zombie</emphasis> status until all children have completed. This operation would require a simple update to the descriptor, which can be executed in a very short time. However, this solution brings to a memory occupation that is not acceptable for our constrained platform. Thus, we opt for a costlier removal of the descriptor from the <emphasis>tree</emphasis>. As a consequence, all child tasks must receive an update from the parent to avoid dangling pointers to a deallocated descriptor.</para>
<para><emphasis role="strong">Taskwait construct:</emphasis> Task-level synchronization is widely used in recursive-based parallel patters. Here typically a fixed number of tasks are created at every recursion level, and their execution is synchronized with a <literal>taskwait</literal> directive. When a parent task encounters a <literal>taskwait</literal>, it should wait until all the children (first-level descendants) have completed, but typically for performance the thread hosting the parent task is allowed to switch to executing one of the children tasks. In the baseline implementation, this feature is supported by just traversing the list of children tasks in the <emphasis>tree</emphasis> data structure and inspecting their status to verify that it is set to WAITING. We changed this mechanism to rely on two queues per task, to directly reference children in the WAITING and RUNNING states, respectively. Upon creation, a task is inserted in the WAITING queue. Every time that a task starts to execute, the runtime moves this task from the WAITING queue to the RUNNING queue, and vice versa in case of suspension. Decoupling waiting and running tasks require a costlier bookkeeping upon task insertion and extraction, but allow faster support for <literal>taskwait</literal> as it is no longer required to search the tree for WAITING tasks. While the benefit brought by this implementation is not evident in the presence of flat parallel patterns, as the <literal>taskwait</literal> is virtually useless in this case, in recursive parallel patterns, it is extensively used and this design choice pays off.</para>
<para><emphasis role="strong">Task dependencies:</emphasis> In the presence of recursive parallel patterns, it is important to distinguish between suspended tasks that could be resumed at any time and tasks that are suspended due to a scheduling constraint that needs to be unblocked. A typical example is, again, tasks suspended upon a <literal>taskwait</literal> or due to a data dependence. As already mentioned, recursive parallelism extensively relies on such a form of synchronization, thus hosting this type of suspended tasks in the same queue that also hosts ready-to-execute tasks used to lead to a situation where we would repeatedly pop from there a task just to realize that the scheduling constraint was still unsatisfied. We would then have to push back the task in the queue and retry. Checking the status of the task before extracting it does not entirely solve the problem, as it requires time-consuming search operations. To deal with this problem, we changed the implementation to avoid re-inserting in the queue suspended tasks with unresolved dependencies. Such tasks are kept floating instead, and it is up to the task that will eventually resolve the dependence to push them back into the queue. This modification requires some additional checks to deal with the above-mentioned case, but greatly improves the performance of recursive parallel programs.</para>
<para><emphasis role="strong">Allocation of runtime metadata:</emphasis> To minimize the overhead for dynamic resource allocation (memory, locks, task descriptors, etc.), we have extensively used pools of pre-allocated resources. This is significantly faster than <emphasis>malloc</emphasis>-like primitives and does not require lock-protected operations, as we adopt thread-private resources. The downside is memory occupation. Since the targeted architecture relies on a shared cluster memory with a limited size, we have to wisely use the available space. A reasonable design solution would be to dedicate roughly 5&#x02013;10% of this memory to hosting tasking support data structures. The original task descriptor has a size of 174 bytes, while the extensions that we introduced require another 98 bytes for the contexts, plus the stacks. Private thread stacks are configured to be 1 KB (a common choice for embedded systems), while task stacks are by default 1/4 of that size. Clearly, all those values are parameters in our design, and can be changed depending on specific application requirements.</para>
<para>Despite the increment of runtime memory requirements, the use of pre-allocated resources enables to exploit finer grained parallelism, which is paramount in current and future embedded systems. Next, we describe solutions to reduce memory pressure and runtime overhead.</para>
<para><emphasis role="strong">Cutoff mechanisms:</emphasis> With 10% of the cluster&#x02019;s shared memory allocated to task descriptors, the runtime can host simultaneously 750 pre-allocated <emphasis>tied</emphasis> tasks or 400 <emphasis>untied</emphasis> tasks. If the queue of available task descriptors is depleted during the program execution, a mechanism (known in the literature as <emphasis>cutoff</emphasis> [11]) is triggered. When this condition is met, the creation of new task descriptors must be suspended to avoid that runtime resources saturate when the task production rate is greater than the execution rate. Our runtime supports two different cutoff variants: <emphasis>yield</emphasis> and <emphasis>work-first</emphasis>. In the first case, the producer task is stopped and pushed at the end of the READY queue, with the aim to re-schedule the core to executing pending tasks instead of generating new ones. Using the second variant, the producer task starts working in work-first mode by executing the new tasks in-place via a standard function call: in this case, task descriptors are not required, as the synchronization is enforced by serializing tasks on the same thread.</para>
<para>Cutoff mechanisms are introduced to avoid an unbounded consumption of runtime resources, but recursive applications can cause additional problems. Using <emphasis>untied</emphasis> tasks, task stacks typically end up to be over-sized to fit the worst case (i.e., the maximum recursion level reached in the cutoff state) to the detriment of runtime memory footprint. To avoid this case, we introduced a specific optimization for <emphasis>untied</emphasis> tasks using <emphasis>work-first</emphasis> cutoff, which forces the producer task to swap its current stack with a special one that is the only one dimensioned for worst case recursive execution.</para>
<para><emphasis role="strong">Support for scheduling policies:</emphasis> The OpenMP runtime provides specific features to support the scheduling policies that have been defined in <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link>. Two alternative implementations are selectable for task queues: <emphasis>global</emphasis> and <emphasis>private</emphasis> queues. The global implementation defines a single task queue for the application, and it is used to support <emphasis>global scheduling</emphasis>. The local implementation instantiates an independent queue per thread, and it is used to support <emphasis>partitioned scheduling</emphasis>, in which tasks are statically allocated to threads at design time.</para>
<para>Adopting a <emphasis>limited preemption</emphasis> scheduler, each TSP in the runtime is considered as a potential preemption point. This is implemented by calling a function designed and implemented for tight integration with the RTOS. The exact behavior depends on the current scheduling policy (<emphasis>global</emphasis> or <emphasis>partitioned</emphasis>) selected for the application, which is totally transparent to the runtime.</para>
<section class="lev2" id="sec6-3-1">
<title>6.3.1 Task Dependency Management</title>
<para>The OpenMP tasking model includes a very mature support for highly unstructured task parallelism with features to express data dependencies (on specific data elements) between tasks. To do so, OpenMP introduces the <literal>depend</literal> clause, which imposes an ordering relation between sibling tasks (tasks that are child tasks of the same task region). OpenMP defines three types of dependencies: <literal>in, out</literal>, and <literal>inout</literal>. A task with an <literal>in</literal> clause cannot start until the set of tasks with an <literal>out</literal> or an <literal>inout</literal> clause on the same data elements complete. This feature is in fact very relevant for embedded systems, often running real-time applications modeled as <emphasis>direct acyclic graphs</emphasis> (DAGs) <footnote id="fn6_2" label="2"> <para>The terms TDG and DAG are equivalent; the former is typically used when referring to runtime methodologies; the latter is used when referring to real-time analysis.</para></footnote> (see <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link> for further information).</para>
<para>Current implementations of the OpenMP tasking model targeting the high-performance domain (e.g., libgomp, nanos++) track data dependencies among tasks by building a <emphasis>task dependency graph</emphasis> (TDG) at runtime. When a new task is created, its <literal>in</literal> and <literal>out</literal> dependencies are matched against those of the existing tasks. To do so, each task region maintains a <emphasis>hash table</emphasis> that stores the memory address of each data element contained within the <literal>out</literal> and <literal>inout</literal> clauses, and the list of tasks associated to it. The hash table is further augmented with links to those tasks depending on it, i.e., including the same data element within the <literal>in</literal> and <literal>inout</literal> clauses. In this way, when the task completes, the runtime can quickly identify its successors, which may be ready to execute.</para>
<para>Building the TDG at runtime requires storing the hash tables in memory until a <literal>taskwait</literal> directive is encountered. Since dependencies can be defined only between sibling tasks, when such directives are encountered, all tasks in their binding region are guaranteed to finish. Moreover, removing the information of a single task at completion would result too costly, because dependent tasks are tracked in multiple linked lists in the hash table. As a result, the memory consumption may significantly increase as the number of instantiated tasks increases.</para>
<para>Such a memory consumption is clearly not a problem in high-performance systems, in which large amounts of memory are available. However, this is not in general the case for parallel embedded architectures. The MPPA processor features only 2 MB of on-chip private memory per cluster. Therefore, it is paramount to devise data structures that reduce to the bare minimum the memory requirements needed to implement the TDG.</para>
<para>To this aim, we maintain the complete OpenMP-DAG generated by the compiler as presented in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>. <emphasis>Although this idea may seem counter-intuitive, the data structures needed to store a statically generated TDG are much lighter than those necessary to dynamically build the TDG</emphasis>. This strategy results in a huge reduction of the memory used at runtime.</para>
<para><emphasis role="strong">TDG Data Structure: A Sparse Matrix &#x02013;</emphasis> A <emphasis>sparse matrix</emphasis> is an optimal solution to store the TDG with minimal footprint. <link linkend="F6-4">Figure <xref linkend="F6-4" remap="6.4"/></link>b shows the sparse matrix implementation of the DAG presented in <link linkend="F6-4">Figure <xref linkend="F6-4" remap="6.4"/></link>a. There, each entry contains a unique task instance identifier <emphasis>t<subscript>id</subscript></emphasis>, and stores in separate arrays the <emphasis>t<subscript>id</subscript></emphasis> and the number of tasks it depends on (labeled <emphasis>Inputs</emphasis> and <emphasis>#in</emphasis> respectively in the figure), and the <emphasis>t<subscript>id</subscript></emphasis> and the number of tasks depending on it (labeled <emphasis>outputs</emphasis> and <emphasis>#out</emphasis> respectively in the figure). Moreover, the sparse matrix is sorted using the <emphasis>t<subscript>id</subscript></emphasis>, so a dichotomic search can be applied.</para>
<para><emphasis>t<subscript>id</subscript></emphasis>, computed with Equation 6.1 (also presented in <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link>), is a key mechanism used to identify the tasks actually instantiated at runtime with those included in the DAG. Therefore, the same value of <emphasis>t<subscript>id</subscript></emphasis> must be generated at compile time (so each node in the DAG has a unique identifier) and at runtime (so tasks can identify its input and output data dependencies).</para>
<fig id="F6-4" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-4">Figure <xref linkend="F6-4" remap="6.4"/></link></label>
<caption><para>On the left (a), the DAG of an OpenMP program. On the right (b), the sparse matrix data structure implementing DAG shown on the left.</para></caption>
<graphic xlink:href="graphics/ch06_esTDG.jpg"/>
</fig>
<para><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/eq6-1.jpg"/></para>
<para>where <emphasis>sid<subscript>t</subscript></emphasis> is a unique task construct identifier, <emphasis>T</emphasis> is equal to the number of <literal>task</literal>, <literal>taskwait</literal>, and <literal>barrier</literal> constructs in the source code, <emphasis>L<subscript>t</subscript></emphasis> is the total number of nested loops involved in the execution of the task <emphasis>t, i</emphasis> refers to the the nesting level, <emphasis>l<subscript>i</subscript></emphasis> is the loop unique identifier at nesting level <emphasis>i</emphasis>, and <emphasis>M</emphasis> is maximum number of iterations of any considered loop.</para>
<para>All the information required to compute Equation 6.1 must therefore be available at compile time. <emphasis>sid<subscript>t</subscript></emphasis> is inserted by the compiler as a new parameter in the function call of the tasking runtime in charge of creating a new OpenMP task (named <literal>GOMP_task</literal>). In order to obtain the same <emphasis>l<subscript>i</subscript></emphasis> at compile-time and at runtime, the compiler introduces a <emphasis>loop stack</emphasis> per loop statement, and <emphasis>push</emphasis> and <emphasis>pop</emphasis> operations before the loop begins and after it ends, respectively. At every loop iteration, the top of the stack is increased by 1. The overhead associated to the stack is very little because it is inserted only in those loops where tasks are created and the overhead due to the task creation dominates. The rest of parameters, i.e., <emphasis>T,L<subscript>t</subscript></emphasis>, and <emphasis>M</emphasis> are encapsulated in the TDG data structure.</para>
<para>Consider task <emphasis>T</emphasis><subscript>4</subscript>, with identifier 79, in <link linkend="F6-4">Figure <xref linkend="F6-4" remap="6.4"/></link>a. This task instance corresponds to the computation of the matrix block <emphasis>m</emphasis>[2,1]. Its identifier is computed as follows: (1) <emphasis>sid</emphasis><subscript><emphasis>T</emphasis>4</subscript> = 4, because <emphasis>T</emphasis><subscript>4</subscript> is the fourth task found in sequential order while traversing the source code; (2) <emphasis>T</emphasis> = 5 because there are four task constructs and one (implicit) barrier in the source code; (3) <emphasis>L</emphasis><subscript><emphasis>T</emphasis><subscript>4</subscript></subscript> = 2, the two nested loops enclosing <emphasis>T</emphasis><subscript>4</subscript>; (4) <emphasis>M</emphasis> = 3, the maximum number of iterations in any of the two considered loops; and (5) <emphasis>l</emphasis><subscript>1</subscript> = 2 and <emphasis>l</emphasis><subscript>2</subscript> = 1 are the values of the loop identifiers at the corresponding iteration. Putting all together: <emphasis>T</emphasis><subscript>4<subscript><emphasis>id</emphasis></subscript></subscript> = 4 + 5(2 &#x02217; 3<superscript>1</superscript> + 1 &#x02217; 3<superscript>2</superscript>) = 79.</para>
<para>Finally, with the objective of monitoring the execution state of task instances, each entry in the sparse matrix has an associated counter (not shown in the figure) describing its state. The counter is:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>-1 if the task has not been instantiated (created) or it has finished;</para></listitem>
<listitem>
<para>0 if the task is ready to run; and</para></listitem>
<listitem>
<para>> 0 if the task is waiting its input tasks to finish. The value indicates the number of tasks created and not completed it still depends on.</para></listitem>
</itemizedlist>
<para>The runtime task scheduler works as follows:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>When a new task is created, the runtime checks the state of its <emphasis>input</emphasis> tasks. If all their counters are -1, the task is ready to execute; otherwise, the state of the counter of the new task is initialized with the number of input tasks with a state &#x02265; 0.</para></listitem>
<listitem>
<para>When a task finishes, it decrements by 1 the counters of all its output tasks whose counter is > 0.</para></listitem>
</itemizedlist>
<para>It is important to remark that, when the TDG contains tasks whose related if-else statement condition has not been determined at compile time and it evaluates to <emphasis>false</emphasis> at runtime, the value of the counter is the same as the tasks would have already finished, i.e., -1 (see <link linkend="ch03">Chapter <xref linkend="ch3" remap="3"/></link> for further information ).</para>
</section>
</section>
<section class="lev1" id="sec6-4">
<title>6.4 Experimental Results</title>
<para>In the following, we present results aimed at characterizing the overheads of the proposed OpenMP runtime design and demonstrating the reduced impact on the overall application performance, compared to different solutions.</para>
<section class="lev2" id="sec6-4-1">
<title>6.4.1 Offoading Library</title>
<para>Synchronization on the Kalray MPPA architecture has a significant impact on the offoading cost. The preliminary implementation of the <emphasis role="strong">BLOCK_OS</emphasis> policy, which has the most complex semantics among all, required 75,500 cycles to initialize the runtime metadata. It is possible to halve the initialization cost by (i) replacing dynamic memory allocation of runtime data structures with a static memory mapping and (ii) distributing between the available cores the initialization of data structures.</para>
<fig id="F6-5" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-5">Figure <xref linkend="F6-5" remap="6.5"/></link></label>
<caption><para>Costs of offoad initialization.</para></caption>
<graphic xlink:href="graphics/ch06_fig6-5.jpg"/>
</fig>
<para>As a further optimization, we implemented a lightweight runtime check of the presence of a pending offoad request to prevent the RTOS from executing the RM when no new offoad requests to process are present. This further reduced the initialization cost to 33,250 cycles. <link linkend="F6-5">Figure <xref linkend="F6-5" remap="6.5"/></link> reports the offoad cost on the cluster side for different synchronization policies. We report minimum and maximum observed execution cycles (blue and orange bars, respectively). The leftmost groups of bars represent the original Kalray software infrastructure, while the three rightmost groups of bars represent the three policies of our software infrastructure. The results for Kalray show a very large variance between minimum and maximum observed offoad cost. Anyhow, since the analysis tools rely on worst case execution time, to all practical purposes, we must consider the maximum time, which is around 82,000 cycles. All the three synchronization policies that we provide exhibit a very small variance, and their cost is in all cases much smaller than the worst case for the original Kalray SDK (roughly in line with the best case).</para>
<para>This notwithstanding, the observed costs for our runtime software are still relevant. Compared to state-of-the-art solution, we identified the main reason of this inefficiency in the management of non-coherent caches. A flush-and-invalidate operation on the data caches is performed at every synchronization point (the <emphasis>unlock</emphasis> primitive). This makes each access to runtime data structures very expensive in terms of execution cycles. Replacing data caches with L1 scratchpad memories and using these memories to store runtime data structures allow reducing the offoad cost by 20x.</para>
</section>
<section class="lev2" id="sec6-4-2">
<title>6.4.2 Tasking Runtime</title>
<para>As already pointed out, supporting the tasking execution model is usually subject to large overheads. While such overheads can be tolerated by large applications exploiting coarse-grained tasks, this is usually not the case for embedded applications, which rely on fine-grained workloads. To study this effect, our plots show speedup (parallel execution on 16 cluster cores versus sequential execution on a single cluster core) on the y-axis, comparing the original Kalray runtime to our runtime support for <emphasis>tied</emphasis> and <emphasis>untied</emphasis> tasks. For all the experiments except the one in Section 6.4.2.5, we use a set of microbenchmarks in which tasks only consist of ALU operations (e.g., <literal>add</literal> on local registers) and no load/store operations, which allows exploring the maximum achievable speedups. The number of ALU operations within the tasks can be controlled via a parameter, which allows studying the achievable speedup for various task granularities, which we report on the x-axis of each plot (task granularity is expressed as duration in clock cycles, roughly equivalent to the number of ALU operations that each task contains).</para>
<para>We consider three variants for the synthetic benchmark: LINEAR, RECURSIVE, and MIXED. These are representative of different task creation patterns found in real applications, and will be described in the following subsections.</para>
<section class="lev2" id="sec6-4-2-1">
<title>6.4.2.1 Applications with a linear generation pattern</title>
<fig id="F6-6" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-6">Figure <xref linkend="F6-6" remap="6.6"/></link></label>
<caption><para>Speedup of the LINEAR benchmark (no cutoff).</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_6.jpg"/>
</fig>
<para>The LINEAR benchmark consists of <emphasis>N</emphasis> = 512 identical tasks, each with a workload of W ALU instructions. The main task creates all the remaining N&#x02026;1 tasks from a simple loop (one task created per loop iteration) and then performs a <literal>taskwait</literal> to ensure that all tasks have completed their execution.</para>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg161-1.jpg"/></para>
<para><link linkend="F6-6">Figure <xref linkend="F6-6" remap="6.6"/></link> shows the results for the LINEAR benchmark. Focusing on the results for the original Kalray SDK (&#x0201C;noCO KALRAY&#x0201D; line), ideal speedups can be achieved only for tasks larger than 100 KCycles. For smaller tasks, the maximum achievable speedup is 3&#x000D7;. In this fine-grain task area, <emphasis>our</emphasis> tasks can consistently achieve a four times higher speedup. Since in the LINEAR microbenchmarks, there is no task nesting, there is no significant difference between <emphasis>tied</emphasis> (PSOC T) and <emphasis>untied</emphasis> (PSOC U) tasks. We thus explore a new configuration where tasks are recursively created to appreciate the difference.</para>
</section>
<section class="lev2" id="sec6-4-2-2">
<title>6.4.2.2 Applications with a recursive generation pattern</title>
<para><link linkend="F6-7">Figure <xref linkend="F6-7" remap="6.7"/></link> shows the efficiency of our runtime for the recursive parallel pattern, considering <emphasis>tied</emphasis> and <emphasis>untied</emphasis> tasks. The RECURSIVE microbenchmark builds a binary tree of depth N = 9 (512 tasks) recursively. This is similar to a classical Fibonacci algorithm, where each of the two recursive calls is enclosed in a <literal>task</literal> directive. A <literal>taskwait</literal> directive is placed after the creation of the two tasks.</para>
<para><inline-graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="graphics/pg162-1.jpg"/></para>
<para>The first result that we observe is that only <emphasis>untied</emphasis> tasks can achieve the maximum speedup. <emphasis>Tied</emphasis> tasks have a maximum speedup of 8. This effect is due to the behavior of <literal>taskwait</literal> in the presence of <emphasis>tied</emphasis> tasks. If a <emphasis>tied</emphasis> task is stuck on a <literal>taskwait</literal> and there are no children tasks in the WAITING state (e.g., few tasks generated at each recursion level, like in the binary tree), that task is bound to wait until the children have finished. Using a binary tree, this leads to exactly half of the threads getting stuck, which explains the maximum speedup observed in this configuration. This problem is circumvented by <emphasis>untied</emphasis> tasks, which can reschedule the threads hosting the stuck tasks to other ready tasks. Similar considerations to what we discussed in the previous section hold for the comparison between Kalray tasks and <emphasis>our</emphasis> <emphasis>tied</emphasis> tasks (Kalray supports only <emphasis>tied</emphasis> tasks, so a comparison to <emphasis>our</emphasis> <emphasis>untied</emphasis> tasks is not directly feasible).</para>
<fig id="F6-7" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-7">Figure <xref linkend="F6-7" remap="6.7"/></link></label>
<caption><para>Speedup of the RECURSIVE benchmark (no cutoff).</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_7.jpg"/>
</fig>
<para>In general, it is possible to see that RECURSIVE implies a much higher overhead than LINEAR. This is justified by a significantly increased contention for shared data structures (queues, trees, etc.), as in this pattern multiple threads are concurrently creating tasks. Even if we have struggled to make the lock-protected operations to operate on shared data structures as short as possible, their serialization over multiple requestors is evident. As a result, it takes an order of magnitude coarser tasks (around 100 K) than in the LINEAR case to achieve nearly ideal speedups. This is a typical situation where cutoff policies can help in significantly reducing the runtime overheads. We explore the adoption of cutoff policies in Section 6.4.2.4.</para>
</section>
<section class="lev2" id="sec6-4-2-3">
<title>6.4.2.3 Applications with mixed patterns</title>
<fig id="F6-8" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-8">Figure <xref linkend="F6-8" remap="6.8"/></link></label>
<caption><para>Structure of the MIXED microbenchmark.</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_8.jpg"/>
</fig>
<para>The advantage of using <emphasis>untied</emphasis> tasks is particularly evident for applications presenting a mixed structure which includes both LINEAR and RECURSIVE task creation patterns. The MIXED microbenchmark depicted in <link linkend="F6-8">Figure <xref linkend="F6-8" remap="6.8"/></link> is aimed at studying the behavior of such applications. A root task generates seven tasks in a LINEAR manner, each one spawning a single child with a long execution time and then performing a <literal>taskwait</literal>, plus another two tasks from within RECURSIVE binary trees of depth 5.</para>
<fig id="F6-9" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-9">Figure <xref linkend="F6-9" remap="6.9"/></link></label>
<caption><para>Speedup of the MIXED benchmark.</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_9.jpg"/>
</fig>
<para><link linkend="F6-9">Figure <xref linkend="F6-9" remap="6.9"/></link> shows the results for this benchmark. Using <emphasis>tied</emphasis> tasks, 14 threads are allocated to execute the linear part of the application, seven of which are blocked by the <literal>taskwait</literal> directive. The ideal speedup of the application is 2, which our <emphasis>tied</emphasis> tasks reach for granularities of around 10 Kcycles.</para>
<para>Using <emphasis>untied</emphasis> tasks, only seven threads are allocated to the LINEAR part, which brings the ideal speedup to 9&#x000D7;. The maximum speedup achieved by our <emphasis>untied</emphasis> tasks is 8, due to a limitation of the tracing (performance monitoring) of the Kalray platform. The root task of the hierarchy is the one performing time measurement and we were forced to declare this as a <emphasis>tied</emphasis> task to gather coherent clock values (allowing this task to migrate to other cores results in incoherent measurement). This limits the maximum achievable speedup to 8&#x000D7;, which our <emphasis>untied</emphasis> tasks achieve for granularities above 10 Kcycles.</para>
<fig id="F6-10" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-10">Figure <xref linkend="F6-10" remap="6.10"/></link></label>
<caption><para>Speedup of the LINEAR benchmark (with cutoff).</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_10.jpg"/>
</fig>
<para>Overall, <emphasis>untied</emphasis> tasks enable four times faster execution than <emphasis>tied</emphasis> tasks for application featuring mixed task creation patterns. Note that this result holds for any runtime implementation. Our solution makes this result visible for smaller tasks compared to other OpenMP tasking implementations. The Kalray implementation never enables any speedup in the considered range of task granularities (up to one million cycles) for this experiment.</para>
</section>
<section class="lev2" id="sec6-4-2-4">
<title>6.4.2.4 Impact of cutoff on LINEAR and RECURSIVE applications</title>
<para>We repeated the experiments with LINEAR and RECURSIVE microbenchmarks considering a higher number of tasks (2,048). This configuration saturates the runtime data structures and activates <emphasis>cutoff</emphasis> mode. Figures 6.10 and 6.11 show the results for this experiment.</para>
<fig id="F6-11" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-11">Figure <xref linkend="F6-11" remap="6.11"/></link></label>
<caption><para>Speedup of the RECURSIVE benchmark (with cutoff).</para></caption>
<graphic xlink:href="graphics/ch06_Figure_6_11.jpg"/>
</fig>
<para>Focusing on the LINEAR pattern, the adoption of cutoff greatly mitigates overhead effects, and we can achieve nearly ideal speedups for an order of magnitude smaller tasks compared to Kalray tasks. It also has to be noted that cutoff mode is not properly supported for LINEAR patterns in the original Kalray runtime. Enabling cutoff mode in this configuration simply seems to disable parallelism completely. Focusing on the RECURSIVE pattern, the use of cutoff policies proves extremely beneficial, with nearly ideal speedups for very fine-grained tasks (in the order of thousand cycles).</para>
</section>
<section class="lev2" id="sec6-4-2-5">
<title>6.4.2.5 Real applications</title>
<para>To assess the performance of our tasking runtime on real applications, we execute the benchmarks from the Barcelona OpenMP Task Suite (BOTS) [12], which includes a wide set of real-life applications parallelized with OpenMP tasks.</para>
<para><link linkend="F6-12">Figure <xref linkend="F6-12" remap="6.12"/></link> shows the speedup of applications for different configurations, comparing the Kalray SDK (KALRAY) with different configurations of our runtime, using <emphasis>tied</emphasis> tasks (PSOC tied), <emphasis>untied</emphasis> tasks (PSOC untied), and <emphasis>untied</emphasis> tasks with cutoff (PSOC untied CO2).</para>
<para>On average, programs executing on top of our runtime show a speedup of 12&#x000D7;, compared to only 8&#x000D7; for the original Kalray SDK. The benefits of cutoff here are minimal, since the bottleneck is limited parallelism in the application rather than runtime overhead. The marginal improvements enabled by cutoff, where present, are usually due to better memory usage (tasks in cutoff use less memory for the runtime, which is used for application data instead).</para>
<fig id="F6-12" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-12">Figure <xref linkend="F6-12" remap="6.12"/></link></label>
<caption><para>Speedups for the BOTS benchmarks.</para></caption>
<graphic xlink:href="graphics/ch06_bots.jpg"/>
</fig>
</section>
</section>
<section class="lev2" id="sec6-4-3">
<title>6.4.3 Evaluation of the Task Dependency Mechanism</title>
<para>This section evaluates the use of a sparse matrix to implement the TDG upon which the task dependency mechanism is built as presented in Section 6.3.1.</para>
<para>Concretely, we implement our task dependency mechanism on top of the GNU libgomp library included in GCC version 4.7.2, which supports tasks but not dependencies, and compare it with the libgomp library included in GCC 4.9.2, which implements a dependency checker based on a hash table structure.</para>
<para>The reason to implement our mechanism on a library not supporting dependencies is that both implementations differ only in the dependency checker, and so being easier to incorporate a new one, rather than replacing it. Moreover, to ensure that results are not affected by the version of the library, we executed the applications considered in this section without dependence clauses. Despite the incorrect result, the numbers revealed that both libraries have the exact same memory usage and performance, demonstrating that the memory increment is exclusively caused by using different dependency checkers.</para>
<para>Moreover, we consider two applications, one from the HPC domain, i.e., a <emphasis>cholesky factorization</emphasis> [13] used for efficient linear equation solvers and Monte Carlo simulations, and one from the embedded domain, i.e., an application resembling the <emphasis>3D path planning</emphasis> [14] (r3DPP) used for airborne collision avoidance.</para>
<para>For comparison purposes, the applications have been parallelized with task dependencies, i.e., using the <literal>depend</literal> clause, and without dependencies, i.e., using only <literal>task</literal> and <literal>taskwait</literal> directives.</para>
<section class="lev2" id="sec6-4-3-1">
<title>6.4.3.1 Performance speedup and memory usage</title>
<para>Figures 6.13 and 6.14 show the performance speedup and the runtime memory usage (in KB) of the Cholesky and r3DPP, when varying the number of instantiated tasks, ranging from 1 to 5984 and 4096, respectively, and considering the three libgomp runtimes implementing a dependency checker based on a hash table, on a sparse matrix, and one with not dependency checker (labeled <emphasis>omp4</emphasis>, <emphasis>omp 3.1</emphasis>, and <emphasis>lightweight omp4</emphasis>, respectively).</para>
<fig id="F6-13" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link></label>
<caption><para>Performance speedup of the Cholesky (a) and r3DPP (b) running with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</para></caption>
<graphic xlink:href="graphics/ch06_fig6-13.jpg"/>
</fig>
<para>The performance has been computed with the average of 100 executions. Similarly, Figures 6.14a,b show the heap memory usage (in KB) of the three OpenMP runtimes when executing Cholesky and r3DPP respectively and varying the number of instantiated tasks as well. The memory usage has been extracted using <emphasis>Valgrind Massif</emphasis> [15] tool, which allows profiling the heap memory consumed by the runtime in which the TDG structure is maintained.</para>
<para>For these experiments, we consider an Intel Xeon CPU E5-2670 processors, featuring eight cores each, with 20 MB L3. The reason is that it incorporates the libgomp library included in GCC 4.9.2 supporting dependency checker based on a hash table.</para>
<fig id="F6-14" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link></label>
<caption><para>Memory usage (in KB) of the Cholesky (a) r3DPP (b) running with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</para></caption>
<graphic xlink:href="graphics/ch06_fig6-14.jpg"/>
</fig>
<para>We observe that both performance and memory usage depend on the number of instantiated tasks: the higher the number of instances, the better the performance, as the chances of parallelism increase. When the number of tasks is too high, however, the overhead introduced by the runtime and the small workload of each task slows down the performance.</para>
<para>As shown in <link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link>, our <emphasis>lightweight omp4</emphasis> obtains the same performance speedups as the <emphasis>omp4</emphasis> implementation for the two applications, and outperforms <emphasis>omp 3.1</emphasis>. However, when observing the memory usage in <link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link>, it rapidly increases for <emphasis>omp4</emphasis>, requiring much more memory than the runtime based on the sparse matrix, i.e., the <emphasis>lightweight omp4</emphasis>.</para>
<para>It is also interesting to observe the parallelization opportunities brought by the <literal>depend</literal> clause, which makes the performance of Cholesky (<link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link>a) to increase significantly compared to not using them, with a speedup increment from 4x to 12x when instantiating 5,984 tasks. At this point, <emphasis>omp4</emphasis> consumes 2.5 MB while our <emphasis>lightweight omp4</emphasis> requires less than 1.3 MB. The memory consumed by <emphasis>omp3.1</emphasis> is less than 100 KB (<link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link>a).</para>
<para>In fact, the <emphasis>omp3.1</emphasis> memory consumption is similar for all the applications because no structure for dependencies management is needed.</para>
<para>For the r3DPP, the <literal>depend</literal> clause achieves a performance speedup of 5.2x and 5.8x with <emphasis>omp4</emphasis> and <emphasis>lightweight omp4</emphasis>, respectively, when instantiating 1,024 tasks (<link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link>b). At this point, <emphasis>omp4</emphasis> consumes 400 KB in front of the 200 KB consumed by <emphasis>lightweight omp4</emphasis> (<link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link>b). Not considering dependencies, i.e., <emphasis>omp31</emphasis>, achieves a maximum performance of 4.5x when 256 tasks are instantiated (<link linkend="F6-13">Figure <xref linkend="F6-13" remap="6.13"/></link>b). When the number of task instances increases to 4096, all runtimes suffer a significant performance degradation because the number of instantiated tasks is too high compared to the workload computed by each task.</para>
<table-wrap position="float" id="T6-1">
<label><link linkend="T6-1">Table <xref linkend="T6-1" remap="6.1"/></link></label>
<caption><para>Memory usage of the sparse matrix (in KB), varying the number of tasks instantiated</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="rows">
<tbody>
<tr>
<td valign="middle" align="center" rowspan="2">Cholesky</td>
<td valign="top" align="center"><emphasis role="strong">Tasks</emphasis></td>
<td valign="top" align="center">4</td>
<td valign="top" align="center">20</td>
<td valign="top" align="center">120</td>
<td valign="top" align="center">816</td>
<td valign="top" align="center">5984</td></tr>
<tr>
<td valign="top" align="center"><emphasis role="strong">KB</emphasis></td>
<td valign="top" align="center">0.11</td>
<td valign="top" align="center">0.59</td>
<td valign="top" align="center">3.80</td>
<td valign="top" align="center">27.09</td>
<td valign="top" align="center">204.19</td></tr>
<tr><td colspan="7"/></tr>
<tr>
<td valign="middle" align="center" rowspan="2">r3DPP</td>
<td valign="top" align="center"><emphasis role="strong">Tasks</emphasis></td>
<td valign="top" align="center">16</td>
<td valign="top" align="center">64</td>
<td valign="top" align="center">256</td>
<td valign="top" align="center">1024</td>
<td valign="top" align="center">4096</td></tr>
<tr>
<td valign="top" align="center"><emphasis role="strong">KB</emphasis></td>
<td valign="top" align="center">00.47</td>
<td valign="top" align="center">1.94</td>
<td valign="top" align="center">7.88</td>
<td valign="top" align="center">31.75</td>
<td valign="top" align="center">127.5</td></tr>
</tbody>
</table>
</table-wrap>
<para><link linkend="T6-1">Table <xref linkend="T6-1" remap="6.1"/></link> shows the size of the sparse matrix data structure implementing the esTDG of each application when varying the number of instantiated tasks (the memory consumption reported in Figures 6.14a,b already includes it).</para>
<fig id="F6-15" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F6-15">Figure <xref linkend="F6-15" remap="6.15"/></link></label>
<caption><para>Performance speedup of the Cholesky (a) and r3DPP (b) running on the MPPA with <emphasis>lightweight omp4, omp4</emphasis>, and <emphasis>omp 3.1</emphasis>, and varying the number of tasks.</para></caption>
<graphic xlink:href="graphics/ch06_fig6-15.jpg"/>
</fig>
</section>
<section class="lev2" id="sec6-4-3-2">
<title>6.4.3.2 The task dependency mechanism on the MPPA</title>
<para>To evaluate the benefit of the task dependency mechanism on a memory constrained manycore architecture, we evaluated it on the MPPA processor. <link linkend="F6-15">Figure <xref linkend="F6-15" remap="6.15"/></link> shows the performance speedup of Cholesky (a) and r3DPP (b) executed in one MPPA cluster, considering the <emphasis>lightweight omp4</emphasis> and <emphasis>omp31</emphasis> runtimes and varying the number of tasks. Note that <emphasis>omp4</emphasis> runtime experiments are not provided because MPPA does not support it. Memory consumption is the same as the one shown in <link linkend="F6-14">Figure <xref linkend="F6-14" remap="6.14"/></link> r3DPP increases the performance speedup from 9x to 12x when using our <emphasis>lightweight omp4</emphasis> rather than <emphasis>omp3.1</emphasis> and only consuming 200 KB. Cholesky presents a significant speedup increment when instantiating 816 tasks, i.e., from 2.5x to 9x, consuming only 220 KB.</para>
</section>
</section></section>
<section class="lev1" id="sec6-5">
<title>6.5 Summary</title>
<para>This chapter has illustrated the design of the OpenMP runtime for a heterogeneous platform including a <emphasis>host</emphasis> processor and an embedded manycore accelerator. The complete software stack is composed of an offoading library and a tasking runtime library, which have been described in detail. The OpenMP runtime provides specific features to support the scheduling policies that have been defined in <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link>, and it also implements the TDG required to support the task dependency mechanism as presented in Section 6.3.1. The chapter has discussed how to enable maximum exploitation of the available hardware parallelism via the <emphasis>untied</emphasis> task model, highlighting the key design choices to achieve low overhead. Experimental results show that this enables up to four times faster execution than <emphasis>tied</emphasis> tasks, which improves on average by 60% over the native Kalray SDK.</para>
</section>
<section class="lev1" id="sec6-6">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Marongiu, A., Capotondi, A., Tagliavini, G., and Benini, L., &#x0201C;Simplifying Many-Core-Based Heterogeneous SoC Programming With Offoad Directives.&#x0201D; In <emphasis>IEEE Transactions on Industrial Informatics</emphasis>, vol. 11, pp. 957&#x02013;967, 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Marongiu%2C+A%2E%2C+Capotondi%2C+A%2E%2C+Tagliavini%2C+G%2E%2C+and+Benini%2C+L%2E%2C+%22Simplifying+Many-Core-Based+Heterogeneous+SoC+Programming+With+O&#xFB04;oad+Directives%2E%22+In+IEEE+Transactions+on+Industrial+Informatics%2C+vol%2E+11%2C+pp%2E+957-967%2C+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Mitra, G., Stotzer, E., Jayaraj, A., and Rendell, A. P., &#x0201C;Implementation and optimization of the OpenMP accelerator model for the TI Keystone II architecture.&#x0201D; In <emphasis>International Workshop on OpenMP</emphasis>, Springer, pp. 202&#x02013;214, 2014. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Mitra%2C+G%2E%2C+Stotzer%2C+E%2E%2C+Jayaraj%2C+A%2E%2C+and+Rendell%2C+A%2E+P%2E%2C+%22Implementation+and+optimization+of+the+OpenMP+accelerator+model+for+the+TI+Keystone+II+architecture%2E%22+In+International+Workshop+on+OpenMP%2C+Springer%2C+pp%2E+202-214%2C+2014%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Rosenstiel, W., and Thiele, L. (editors), <emphasis>Design, Automation and Test in Europe Conference and Exhibition, DATE 2012, Dresden, Germany.</emphasis> IEEE, 2012.</para></listitem>
<listitem>
<para>Podobas, A., Brorsson, M., and Fax[x00E9;]n, K.-F., A comparative performance study of common and popular task-centric programming frameworks. <emphasis>Concurr. Comput. Pract. Exp.</emphasis> 27, 1&#x02013;28, 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Podobas%2C+A%2E%2C+Brorsson%2C+M%2E%2C+and+Fax%5Bx00E9%3B%5Dn%2C+K%2E-F%2E%2C+A+comparative+performance+study+of+common+and+popular+task-centric+programming+frameworks%2E+Concurr%2E+Comput%2E+Pract%2E+Exp%2E+27%2C+1-28%2C+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Duran, A., Teruel, X., Ferrer, R., Martorell, X., and Ayguade, E., &#x0201C;Barcelona OpenMP Tasks Suite: A Set of Benchmarks Targeting the Exploitation of Task Parallelism in OpenMP.&#x0201D; In  <emphasis>2009 International Conference on Parallel Processing</emphasis>, pp. 124&#x02013;131. IEEE, 2009. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Duran%2C+A%2E%2C+Teruel%2C+X%2E%2C+Ferrer%2C+R%2E%2C+Martorell%2C+X%2E%2C+and+Ayguade%2C+E%2E%2C+%22Barcelona+OpenMP+Tasks+Suite%3A+A+Set+of+Benchmarks+Targeting+the+Exploitation+of+Task+Parallelism+in+OpenMP%2E%22+In++2009+International+Conference+on+Parallel+Processing%2C+pp%2E+124-131%2E+IEEE%2C+2009%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Burgio, P., Tagliavini, G., Marongiu, A., and Benini, L., &#x0201C;Enabling fine-grained OpenMP tasking on tightly-coupled shared memory clusters.&#x0201D; In <emphasis>Proceedings of the Conference on Design, Automation and Test in Europe</emphasis>, DATE &#x02019;13, pp. 1504&#x02013;1509. EDA Consortium, 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Burgio%2C+P%2E%2C+Tagliavini%2C+G%2E%2C+Marongiu%2C+A%2E%2C+and+Benini%2C+L%2E%2C+%22Enabling+fine-grained+OpenMP+tasking+on+tightly-coupled+shared+memory+clusters%2E%22+In+Proceedings+of+the+Conference+on+Design%2C+Automation+and+Test+in+Europe%2C+DATE+%2713%2C+pp%2E+1504-1509%2E+EDA+Consortium%2C+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Rochange, C., Bonenfant, A., Sainrat, P., Gerdes, M., Lobo, J., et al., &#x0201C;WCET analysis of a parallel 3D multigrid solver executed on the MERASA multi-core.&#x0201D; In  <emphasis>WCET</emphasis>, 2010. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Rochange%2C+C%2E%2C+Bonenfant%2C+A%2E%2C+Sainrat%2C+P%2E%2C+Gerdes%2C+M%2E%2C+Lobo%2C+J%2E%2C+et+al%2E%2C+%22WCET+analysis+of+a+parallel+3D+multigrid+solver+executed+on+the+MERASA+multi-core%2E%22+In++WCET%2C+2010%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Kumar, S., Hughes, C. J., and Nguyen, A, &#x0201C;Carbon: Architectural Support for Fine-grained Parallelism on Chip Multiprocessors.&#x0201D; In <emphasis>Proceedings of the 34th Annual International Symposium on Computer Architecture</emphasis>, ISCA &#x02019;07, pp. 162&#x02013;173. ACM, 2007. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Kumar%2C+S%2E%2C+Hughes%2C+C%2E+J%2E%2C+and+Nguyen%2C+A%2C+%22Carbon%3A+Architectural+Support+for+Fine-grained+Parallelism+on+Chip+Multiprocessors%2E%22+In+Proceedings+of+the+34th+Annual+International+Symposium+on+Computer+Architecture%2C+ISCA+%2707%2C+pp%2E+162-173%2E+ACM%2C+2007%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Serrano, M. A., Melani, A., Vargas, R., Marongiu, A., Bertogna, M., and Qui[x00F1;]ones, E., &#x0201C;Timing Characterization of OpenMP4 Tasking Model.&#x0201D; In <emphasis>Proceedings of the 2015 International Conference on Compilers, Architecture and Synthesis for Embedded Systems</emphasis>, CASES &#x02019;15, pp. 157&#x02013;166. IEEE Press, 2015. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Serrano%2C+M%2E+A%2E%2C+Melani%2C+A%2E%2C+Vargas%2C+R%2E%2C+Marongiu%2C+A%2E%2C+Bertogna%2C+M%2E%2C+and+Qui%5Bx00F1%3B%5Dones%2C+E%2E%2C+%22Timing+Characterization+of+OpenMP4+Tasking+Model%2E%22+In+Proceedings+of+the+2015+International+Conference+on+Compilers%2C+Architecture+and+Synthesis+for+Embedded+Systems%2C+CASES+%2715%2C+pp%2E+157-166%2E+IEEE+Press%2C+2015%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Marlin, C. D., <emphasis>Coroutines: a programming methodology, a language design and an implementation</emphasis>. Number 95 in Lecture Notes in Computer Science. Springer Science and Business Media, 1980. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Marlin%2C+C%2E+D%2E%2C+Coroutines%3A+a+programming+methodology%2C+a+language+design+and+an+implementation%2E+Number+95+in+Lecture+Notes+in+Computer+Science%2E+Springer+Science+and+Business+Media%2C+1980%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Duran, A., Corbal[x00E1;]n, J., and Ayguad[x00E9;], E., &#x0201C;Evaluation of OpenMP task scheduling strategies.&#x0201D; In <emphasis>International Workshop on OpenMP</emphasis>, pp. 100&#x02013;110. Springer, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Duran%2C+A%2E%2C+Corbal%5Bx00E1%3B%5Dn%2C+J%2E%2C+and+Ayguad%5Bx00E9%3B%5D%2C+E%2E%2C+%22Evaluation+of+OpenMP+task+scheduling+strategies%2E%22+In+International+Workshop+on+OpenMP%2C+pp%2E+100-110%2E+Springer%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Duran, A., Corbalan, J., and Ayguade, E., &#x0201C;An adaptive cut-off for task parallelism.&#x0201D; In <emphasis>2008 SC &#x02013; International Conference for High Performance Computing, Networking, Storage and Analysis</emphasis>, pp. 1&#x02013;11. IEEE, 2008. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Duran%2C+A%2E%2C+Corbalan%2C+J%2E%2C+and+Ayguade%2C+E%2E%2C+%22An+adaptive+cut-off+for+task+parallelism%2E%22+In+2008+SC+-+International+Conference+for+High+Performance+Computing%2C+Networking%2C+Storage+and+Analysis%2C+pp%2E+1-11%2E+IEEE%2C+2008%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Bascelija, N., Sequential and Parallel Algorithms for Cholesky Factorization of Sparse Matrices. <emphasis>WSEAS: Mathematic. Appl. Sci. Mech.</emphasis> 2013. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Bascelija%2C+N%2E%2C+Sequential+and+Parallel+Algorithms+for+Cholesky+Factorization+of+Sparse+Matrices%2E+WSEAS%3A+Mathematic%2E+Appl%2E+Sci%2E+Mech%2E+2013%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Cesarini, D., Marongiu, A., and Benini, L., &#x0201C;An optimized task-based runtime system for resource-constrained parallel accelerators.&#x0201D; In <emphasis>2016 Design, Automation and Test in Europe Conference and Exhibition, DATE 2016</emphasis>, Dresden, Germany, pp. 1261&#x02013;1266, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Cesarini%2C+D%2E%2C+Marongiu%2C+A%2E%2C+and+Benini%2C+L%2E%2C+%22An+optimized+task-based+runtime+system+for+resource-constrained+parallel+accelerators%2E%22+In+2016+Design%2C+Automation+and+Test+in+Europe+Conference+and+Exhibition%2C+DATE+2016%2C+Dresden%2C+Germany%2C+pp%2E+1261-1266%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para>Nethercote, N., et. al., &#x0201C;Building Workload Characterization Tools with Valgrind.&#x0201D; In <emphasis>IISWC</emphasis>, 2006. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Nethercote%2C+N%2E%2C+et%2E+al%2E%2C+%22Building+Workload+Characterization+Tools+with+Valgrind%2E%22+In+IISWC%2C+2006%2E" target="_blank">Google Scholar</ulink></para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="chapter" id="ch07" label="7" xreflabel="7">
<title>Embedded Operating Systems</title>
<para><emphasis role="strong">Claudio Scordino<superscript><emphasis role="strong">1</emphasis></superscript>, Errico Guidieri<superscript><emphasis role="strong">1</emphasis></superscript>, Bruno Morelli<superscript><emphasis role="strong">1</emphasis></superscript>, Andrea Marongiu<superscript><emphasis role="strong">2,3</emphasis></superscript>, Giuseppe Tagliavini<superscript><emphasis role="strong">3</emphasis></superscript> and Paolo Gai<superscript><emphasis role="strong">1</emphasis></superscript></emphasis></para>
<para><superscript>1</superscript>Evidence SRL, Italy</para>
<para><superscript>2</superscript>Swiss Federal Institute of Technology in Zurich (ETHZ), Switzerland</para>
<para><superscript>3</superscript>University of Bologna, Italy</para>
<para>In this chapter, we will provide a description of existing open-source operating systems (OSs) which have been analyzed with the objective of providing a porting for the reference architecture described in <link linkend="ch02">Chapter <xref linkend="ch2" remap="2"/></link>. Among the various possibilities, the ERIKA Enterprise RTOS (Real-Time Operating System) and Linux with preemption patches have been selected. A description of the porting effort on the reference architecture has also been provided.</para>
<section class="lev1" id="sec7-1">
<title>7.1 Introduction</title>
<para>In the past, OSs for high-performance computing (HPC) were based on custom-tailored solutions to fully exploit all performance opportunities of supercomputers. Nowadays, instead, HPC systems are being moved away from in-house OSs to more generic OS solutions like Linux. Such a trend can be observed in the TOP500 list [1] that includes the 500 most powerful supercomputers in the world, in which Linux dominates the competition. In fact, in around 20 years, Linux has been capable of conquering all the TOP500 list from scratch (for the first time in November 2017).</para>
<fig id="F7-1" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F7-1">Figure <xref linkend="F7-1" remap="7.1"/></link></label>
<caption><para>Number of Linux-based supercomputers in the TOP500 list.</para></caption>
<graphic xlink:href="graphics/ch07_fig7_1.jpg"/>
</fig>
<para>Each manufacturer, however, still implements specific changes to the Linux OS to better exploit specific computer hardware features. This is especially true in the case of computing nodes in which lightweight kernels are used to speed up the computation.</para>
<para>Linux is a full-featured OS, originally designed to be used in server or desktop environments. Since then, Linux has evolved and grown to be used in almost all computer areas &#x02013; among others, embedded systems and parallel clusters. Linux currently supports almost every hardware processor, including x86, x86-64, ARM, PowerPC, MIPS, SuperH, IBM S/390, Motorola 68000, SPARC, etc. The programmability and the portability of code across different systems are ensured by the well-known &#x0201C;Portable Operating System Interface&#x0201D; (POSIX) API. This is an IEEE standard defining the basic environment and set of functions offered by the OS to the application programs.</para>
<para>Hence, the main reason for this success and popularity in the HPC sector is its excellent performance and its extreme scalability, due to very carefully designed data structures like Linux Read-Copy Update (RCU) [2]. This scalability, together with the high modularity, enables excellent performance on both a powerful parallel cluster made by thousands of cores and a small embedded microcontroller, as will be shown in the next sections.</para>
<para>Therefore, when designing the support for our predictable parallel programming framework, we started selecting Linux as the basic block for executing the target parallel applications. On the other hand, Linux alone is not sufficient for implementing the needed runtime support on our reference architecture: a solution needed to be found for the compute cores, where a tiny RTOS is needed in order to provide an efficient scheduling platform to support the parallel runtime described in <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link>.</para>
<para>This chapter in particular describes in detail how the scheduling techniques designed in <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link> have been implemented on the reference architecture. The chapter includes notes about the selection of the tiny RTOS for the compute cores, with a description of the RTOS, as well as the solutions implemented to support Linux on the I/O cores with real-time performance.</para>
<para>This chapter is structured as follows. Section 7.2 describes the state of the art of the real-time support for the Linux OS and as well for small RTOSes. Section 7.3 describes the requirements that influenced the choice of the RTOS, which is described in detail in Section 7.4. Section 7.5 provides some insights about the OS support for the host processor and for the many-core processor. Finally, Section 7.6 summarizes the chapter.</para>
</section>
<section class="lev1" id="sec7-2">
<title>7.2 State of The Art</title>
<section class="lev2" id="sec7-2-1">
<title>7.2.1 Real-time Support in Linux</title>
<para>As noted in the Section &#x0201C;Introduction,&#x0201D; in the last years, there has been a considerable interest in using Linux for both HPC and real-time control systems, from academic institutions, independent developers, and industries. There are several reasons for this rising interest.</para>
<para>First of all, Linux is an Open Source project, meaning that the source code of the OS is freely available to everybody, and can be customized according to user needs, provided that the modified version is still licensed under the GNU General Public License (GPL) [3]. This license allows anybody to redistribute, and even sell, a product as long as the recipient is able to exercise the same rights (access to the source-code included). This way, a user (for example, a company) is not tied to the OS provider anymore, and is free to modify the OS at will. The Open Source license helped the growth of a large community of researchers and developers who added new features to the kernel and ported Linux to new architectures. Nowadays, there is a huge number of programs, libraries, and tools available as Open Source code that can be used to build a customized version of the OS.</para>
<para>Moreover, Linux has the simple and elegant design of the UNIX OSs, which guarantees meeting the typical reliability and security requirements of real-time systems.</para>
<para>Finally, the huge community of engineers and developers working on Linux makes finding expert programmers very easy.</para>
<para>Unfortunately, the standard mainline kernel (as provided by Linus Torvalds) is not adequate to be used as RTOS. Linux has been designed to be a general-purpose operating system (GPOS), and thus not much attention has been given to the problem of reducing the latency of critical operations. Instead, the main design goal of the Linux kernel has been (and still remains) to optimize the average throughput (i.e., the amount of &#x0201C;useful work&#x0201D; done by the system in the unit of time). For this reason, a Linux program may suffer a high latency in response to critical events. To overcome these problems, many approaches have been proposed in the last years to modify Linux in order to make it more &#x0201C;real-time.&#x0201D; These approaches can be grouped in the following classes [4]:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Hard real-time scheduling through a Hardware Abstraction Layer (HAL);</para></listitem>
<listitem>
<para>Latency reduction through better preemption mechanisms and interrupt handling;</para></listitem>
<listitem>
<para>Proper real-time scheduling policies.</para></listitem>
</orderedlist>
<para>The following subsections describe each approach in detail.</para>
<section class="lev2" id="sec7-2-1-1">
<title>7.2.1.1 Hard real-time support</title>
<para>This approach consists in creating a layer of virtual hardware between the standard Linux kernel and the real hardware. This layer is called Real-Time Hardware Abstraction Layer (RTHAL). It abstracts the hardware timers and interrupts and adds a separate subsystem to run the real-time tasks. The Linux kernel and all the normal Linux processes are then managed by the abstraction layer as the lowest priority tasks &#x02014; i.e., the Linux kernel only executes when there are no real-time tasks to run.</para>
<para>The first project implementing this approach was RTLinux [5]. The project started at Finite State Machine Labs (FSMLabs) in 1995. Then, it was released in two different versions: an Open Source version (under GPL license) and a more featured commercial version. An operation of patenting issued in US in 1999, however, generated a massive transition of developers towards the parallel project RTAI. Then, the commercial version was bought by WindRiver. Nowadays, both versions are not maintained anymore [5].</para>
<para>RTAI [6] (which stands for &#x0201C;Real-Time Application Interface&#x0201D;) is a project started as a variant of RTLinux in 1997 at Dipartimento di Ingegneria Aerospaziale of Politecnico di Milano (DIAPM), Italy. The project is under LGPL license, and it was supported by a large community of developers, based on the Open Source model. Although the project initially started from the original RTLinux code, it has been completely rewritten over time. In particular, the RTAI community has developed the Adaptive Domain Environment for Operating Systems (ADEOS) nanokernel as an alternative for RTAI&#x02019;s core, to get rid of the old kernel patch and exploit a more structured and flexible way to add a real-time environment to Linux [4]. The project mainly targets the x86 architecture and is currently maintained (even if less popular than it used to be in the past).</para>
<para>Xenomai [7] was born in 2001 as an evolution of Fusion, a project to run RTAI tasks in the user space. With Xenomai, a real-time task can execute in user space or in kernel space. Normally, it starts in kernel space (i.e., &#x0201C;primary domain&#x0201D;), where it has real time performance. When the real-time task invokes a function belonging to the Linux standard API or libraries, it is automatically migrated to the user-level (i.e., &#x0201C;secondary domain&#x0201D;), under the control of the Linux scheduler. In this secondary domain, it keeps a high priority, being scheduled with the SCHED FIFO or SCHED RR Linux policies. However, it can experience some delay and latency, due to the fact that it is scheduled by Linux. After the function call has been completed, the task can go back to the primary mode by explicitly calling a function. In this way, at the cost of some limited unpredictability, the real-time programmer can use the full power of Linux also for real-time applications.</para>
<para>Among the various projects implementing the hardware abstraction approach, Xenomai is the one which supports the highest number of embedded architectures. It supports ARM, Blackfin, NiosII, PPC and, of course, x86. Xenomai also offers a set of skins implementing the various APIs of popular RTOS such as Windriver VxWorks [8], as well as the POSIX API [9]. In version 3 of Xenomai, the project aims at working on top of both a native Linux kernel and a kernel with PREEMPT_RT [10], by providing a set of user-space libraries enabling seamless porting of applications among the various OS versions.</para>
<para>It is important to highlight the advantages of the approach of hardware abstraction. First of all, the latency reduction is really effective [4]. This allows the implementation of very fast control loops for applications like vibrational control. Moreover, it is possible to use a full-featured OS like Linux for both the real-time and the non-real-time activities (e.g., HMI, logging, monitoring, communications, etc.). Finally, the possibility of developing and then executing the code on the same hardware platform, considerably simplifies the complexity of the development environment.</para>
<para>Typical drawbacks of this approach &#x02013; which depend on the particular implementation &#x02013; are:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Real-time tasks must be implemented using specific APIs, and they cannot access typical Linux services without losing their real-time guarantees.</para></listitem>
<listitem>
<para>The implementation is very hardware-dependent, and may not be available for a specific architecture.</para></listitem>
<listitem>
<para>The real-time tasks are typically executed as modules dynamically loaded into the kernel. Thus, there is no memory protection and a buggy real-time task may crash the whole system.</para></listitem>
</itemizedlist>
<para>For these reasons, this approach is usually followed only to build hard real-time systems with very tight requirements.</para>
</section>
<section class="lev2" id="sec7-2-1-2">
<title>7.2.1.2 Latency reduction</title>
<para>&#x0201C;Latency&#x0201D; can be defined as the time between the occurrence of an event and the beginning of the action to respond to the event [4]. In the case of an OS, it is often defined as the time between the interrupt signal arriving to the processor (signaling the occurrence of an external event like data from a sensor) and the time when the handling routine starts execution (e.g., the real-time task that responds to the event). Since in the development of critical real-time control systems, it is necessary to account for the worst-case scenario, a particularly important measure is the maximum latency value.</para>
<para>The two main sources of latency in general-purpose OSs are task latency and timer resolution:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>Task latency is experienced by a process when it cannot preempt a lower priority process because this is executing in kernel context (i.e., the kernel is executing on behalf of the process). Typically, monolithic OSs do not allow more than one stream of execution in kernel context, so that the high-priority task cannot execute until the kernel code either returns to user-space or explicitly blocks. As we will explain in the following paragraphs, Linux has been capable of mixing the advantages of a traditional monolithic design with the performance of concurrent streams of execution within the kernel.</para></listitem>
<listitem>
<para>The timer resolution depends on the frequency at which the electronics issues the timing interrupts (also called &#x0201C;tick&#x0201D;). This hardware timer is programmed by the OS to issue interrupts at a pre-programmed period of time. The periodic tick rate directly affects the granularity of all timing activities. The Linux kernel has recently switched towards a dynamic tick timer, where the timer does not issue interrupts at a periodic rate. This feature allows the reduction of energy consumption whenever the system is idle.</para></listitem>
</orderedlist>
<para>In the course of the years, several strategies have been designed and implemented by kernel developers to reduce these values. Among the mechanisms already integrated in the official Linux kernel, we can find:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Robert Love&#x02019;s Preemptible Kernel patch to make the Linux kernel preemptible just like user-space. This means that several flows of kernel execution can be run simultaneously. Urgent events can be served regardless of the fact that the system is running in the kernel context. Hence, it becomes possible to preempt a process at any point, as long as the kernel is in a consistent state. With this patch the Linux kernel has become a fully preemptive kernel, unlike most existing OSs (UNIX variants included). This feature was introduced in the 2.6 kernel series (December 2003).</para></listitem>
<listitem>
<para>High Resolution Timers (HRT) is a mechanism to issue timer interrupts aperiodically &#x02013; i.e., the system timer is programmed to generate the interrupt after an interval of time that is not constant, but depends on the next event scheduled by the OS. Often, these implementations also exploit processor-specific hardware (like the APIC on modern x86 processors) to obtain a better timing resolution. This feature was introduced in the 2.6.16 kernel release (March 2006).</para></listitem>
<listitem>
<para>Priority inheritance for user-level mutex, available since release 2.6.18 (September 2006). Priority inheritance support is useful to guarantee bounded blocking times in case more than one thread needs to concurrently access the same resource. The main idea is that blocking threads inherit the priority of the blocked threads, thus giving them additional importance in order to finish their job early.</para></listitem>
<listitem>
<para>Threaded interrupts by converting interrupt handlers into preemptible kernel threads, available since release 2.6.30 (June 2009). To better understand the effect of this patch, we have to consider that the typical way interrupts are managed in Linux is to manage the effect of the interruption immediately inside the so-called interrupt handler. In this way, peripherals are handled immediately, typically providing a better throughput (because thread waiting for asynchronous events are put earlier in the ready queue). On the other hand, a real-time system may have a few &#x0201C;important&#x0201D; IRQs that need immediate service, while the others, linked to lower priority activities (e.g., network, disk I/O), can experiences higher delays. Therefore, having all interrupt services immediately may provide unwanted jitter in the response times, as low-priority IRQ handlers may interrupt high-priority tasks. The threaded interrupt patch solves this problem by transforming all IRQ handlers into kernel threads. As a result, the IRQ handlers (and their impact on the response time) are minimized. Moreover, users can play with real-time priorities to eventually raise the priorities of the important interrupts, therefore providing stronger real-time guarantees.</para></listitem>
</itemizedlist>
<para>PREEMPT_RT [10] is an on-going project supported by the Linux Foundation [11] to bring real-time performance to a further level of sophistication, by introducing preemptible (&#x0201C;sleeping&#x0201D;) spinlocks and RT mutexes implementing Priority Inheritance to avoid priority inversion.</para>
<para>It is worth specifying that the purpose of the PREEMPT_RT patch is not to improve the throughput or the overall performance. The patch aims at reducing the maximum latency experienced by an application to make the system more predictable and deterministic. The average latency, however, is often increased.</para>
</section>
<section class="lev2" id="sec7-2-1-3">
<title>7.2.1.3 Real-time CPU scheduling</title>
<para>Linux systems traditionally offered only two kind of scheduling policies:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>SCHED_OTHER: Best-effort round-robin scheduler;</para></listitem>
<listitem>
<para>SCHED_FIFO/SCHED_RR: Fixed-priority POSIX scheduling.</para></listitem>
</orderedlist>
<para>During the last decade, due to the increasing need of a proper real-time scheduler, a number of projects have been proposed to add more sophisticated real-time scheduling (e.g., SCHED_SOFTRR [12], SCHED_ISO [13], etc.). However, they remained as separate projects and have never been integrated in the mainline kernel.</para>
<para>During the last years, the real-time scheduler SCHED_DEADLINE [14, 15] originally proposed and developed by Evidence Srl in the context of the EU project ACTORS [16], has been integrated in the Linux kernel. It is available since the stable release 3.14 (March 2014). It consists of a platform-independent real-time CPU scheduler based on the Earliest Deadline Scheduler (EDF) algorithm [17], and it offers temporal isolation between the running tasks. This means that the temporal behavior of each task (i.e., its ability to meet its deadlines) is not affected by the behavior of the other tasks running in the system. Even if a task misbehaves, it is not able to exploit larger execution times than the amount it has been allocated. The scheduler only enforces temporal isolation on the CPU, and it does not yet take into account shared hardware resources that could affect the timing behavior.</para>
<para>A recent collaboration between Scuola Superiore Sant&#x02019;Anna, ARM Ltd. and Evidence Srl, has aimed at overcoming the non-work-conserving nature of SCHED_DEADLINE while keeping the real-time predictability. This joint effort that replaced the previous CBS algorithm with GRUB has been merged since kernel release 4.13.</para>
</section>
</section>
<section class="lev2" id="sec7-2-2">
<title>7.2.2 Survey of Existing Embedded RTOSs</title>
<para>The market of embedded RTOSs has been exploited in the past decades by several companies that have been able to build solid businesses. These companies started several years ago, when the competition from free OSs was non-existent or very low. Thus, they had enough time to create a strong business built on top of popular and reliable products. Nowadays, the market is full of commercial solutions, which differentiate in the application domain (e.g., automotive, avionics, railway, etc.) and in the licensing model. Most popular commercial RTOSs are: Windriver VxWorks [8], Green Hills Integrity [18], QNX [19], SYSGO PikeOS [20], Mentor Graphics Nucleus RTOS [21], LynuxWorks LynxOS [22], and Micrium &#x003BC;c/OS-III [23]. However, there are some other interesting commercial products like Segger EmbOS [24], ENEA OSE [25], and Arccore Arctic core [26].</para>
<para>On the other hand, valid Open-Source alternatives exist. The development of a completely free software tool chain being our target, the focus of this subsection will be more on the free RTOSs available publicly. Some free RTOSs, in fact, have now reached a level of maturity in terms of reliability and popularity that can compete with commercial solutions. The Open-Source licenses allow the modification of the source code and porting the RTOS on the newest many-core architectures.</para>
<para>This section provides an overview of the free RTOSs available. For each RTOS, the list of supported architectures, the level of maturity and the kind of real-time support are briefly provided. Other information about existing RTOSs can be found in [27].</para>
<section class="lev2" id="sec7-2-2-1">
<title>FreeRTOS</title>
<para>FreeRTOS [28] is a small RTOS written in C. It provides threads, tasks, mutexes, semaphores and software timers. A tick-less mode is provided for low-power applications.</para>
<para>It supports several architectures, including ARM (ARM7/9, Cortex-A/M), Altera Nios2, Atmel AVR and AVR32, Cortus, Cypress PSoC, Freescale Coldfire and Kinetis, Infineon TriCore, Intel x86, Microchip dsPIC and PIC18/24/32 and dsPIC, Renesas H8/S, SuperH, Fujitsu, Xilinx Microblaze, etc.</para>
<para>It does not implement very advanced scheduling algorithms, but it offers a classical preemptive or cooperative fixed-priority round-robin with priority inheritance mutexes.</para>
<para>The RTOS is Open Source, and was initially distributed under a license similar to GPL with linking exception [29]. Recently the FreeRTOS kernel has been relicensed under the MIT license thanks to the collaboration with Amazon AWS. A couple of commercial versions called SafeRTOS and OpenRTOS are available as well. The typical footprint is between 5 KB and 10 KB.</para>
</section>
<section class="lev2" id="sec7-2-2-2">
<title>Contiki</title>
<para>Contiki [30] is an Open-Source OS for networked, memory-constrained systems with a particular focus on low-power Internet of things devices. It supports about a dozen microcontrollers, even if the ARM architecture is not included. The Open-Source license is BSD, which allows the usage of the OS in commercial devices without releasing proprietary code.</para>
<para>Although several resources include Contiki in the list of free RTOSs, Contiki is not a proper RTOS. The implementation is based on the concept of protothreads, which are non-preemptible stack-less threads [31]. Context switch can only take place on blocking operations, and does not preserve the content of the stack (i.e., global variables must be used to maintain variables across context switches).</para>
<para>Stack sharing is a useful feature, but the lack of preemptive support and advanced scheduling mechanisms made this OS not suitable to meet the needs of the parallel programming software framework we want to implement.</para>
</section>
<section class="lev2" id="sec7-2-2-3">
<title>Marte OS</title>
<para>Marte OS [32] is a hard RTOS that follows the Minimal Real-Time POSIX.13 subset. It has been developed by the University of Cantabria. Although it is claimed to be designed for the embedded domain, the only supported platform is the x86 architecture. The development is discontinued, and the latest contributions date back to June 2011.</para>
</section>
<section class="lev2" id="sec7-2-2-4">
<title>Ecos and EcosPro</title>
<para>Ecos [33] is an Open-Source RTOS for applications which need only one process with multiple threads. The source code is under GNU GPL with linking exception.</para>
<para>The current version is 3.0 and it runs on a wide variety of hardware architectures, including ARM, CalmRISC, Motorola 68000/Coldfire, fr30, FR-V, Hitachi H8, IA32, MIPS, MN10300, OpenRISC, PowerPC, SPARC, SuperH, and V8xx.</para>
<para>The footprint is in the order of tens of KB, which does not make it suitable for processing units with extremely low memory. The kernel is currently developed in a closed-source fork named eCosPro.</para>
</section>
<section class="lev2" id="sec7-2-2-5">
<title>FreeOSEK</title>
<para>FreeOSEK [34] is a minimal RTOS implementing the OSEK/VDX automotive standard, like Erika Enterprise. The Open-Source license (GNU GPLv3 with linking exception) is similar to the one of Erika Enterprise too. However, it only supports the ARM7 architecture, the development community is small, and the project does not appear to be actively maintained.</para>
</section>
<section class="lev2" id="sec7-2-2-6">
<title>QP</title>
<para>Quantum platform (QP) [35] is a family of lightweight, open source software frameworks developed by company Quantum Leaps. These frameworks allow building modular real-time embedded applications as systems of cooperating, event-driven active objects (actors). In particular, QK (Quantum Kernel) is a tiny preemptive non-blocking run-to-completion kernel designed specifically for executing state machines in a run-to-completion (RTC) fashion.</para>
<para>Quantum platform supports several microcontrollers, including ARM Cortex-M, ARM 7/9/Cortex-M, Atmel AVR Mega and AVR32, Texas Instruments MSP430/TMS320C28x/TMS320C55x, Renesas Rx600/R8C/H8, Freescale Coldfire/68HC08, Altera Nios II, Microchip PIC24/dsPIC, and Cypress PSoC1.</para>
<para>The software is released in dual licensing: an Open-Source and a commercial license. The Open-Source license is GNU GPL v3, which requires the release of the source code to any end user. Unfortunately, the Open-Source license chosen is not suitable for consumer electronics, where the companies want to keep the intellectual property of their application software.</para>
</section>
<section class="lev2" id="sec7-2-2-7">
<title>Trampoline</title>
<para>Trampoline [36] is an RTOS which aims at OSEK/VDX automotive certification. However, unlike ERIKA Enterprise, it has not yet been certified.</para>
<para>Only the following architectures are supported: Cortex M, Cortex A7 (alone or with the Hypervisor XVisor), RISC-V, PowerPC 32 bits, AVR, ARM 32 bit.</para>
<para>The Open-Source license at the time the evaluation was made was LGPL v2.1. This license is not very suitable for consumer electronics because it implies that any receiver of the binary (e.g., final user buying a product) must be given access to the low-level and the possibility of relinking the application towards a newer version of the RTOS. The license was changed afterwards to GPL v2 in September 2015.</para>
</section>
<section class="lev2" id="sec7-2-2-8">
<title>RTEMS</title>
<para>RTEMS [37] is a fully-featured Open-Source RTOS supporting several application programming interfaces (APIs) such as POSIX and BSD sockets. It is used in several application domains (e.g., avionics, medical, networking) and supports a wide range of architectures including ARM, PowerPC, Intel, Blackfin, MIPS, and Microblaze. It implements a single process, multithreaded environment. The Open-Source license is similar (but not equal) to the more popular GPL with Linking Exception [29].</para>
<para>The footprint is not extremely small, and for the smallest applications, ranges from 64 to 128 K on nearly all CPU families [38]. For this reason, another project called TinyRTEMS [39] has been created to reduce the footprint of RTEMS. However, its Open-Source license is GPLv2, which is not suitable for development in industrial contexts.</para>
</section>
<section class="lev2" id="sec7-2-2-9">
<title>TinyOS</title>
<para>TinyOS [40] is an Open-Source OS specifically designed for low-power wireless devices (e.g., sensor networks) and mainly used in research institutions. It has been designed for very resource-constrained devices, such as microcontrollers with a few KB of RAM and a few tens of KB of code space. It&#x02019;s also been designed for devices that need to be very low power.</para>
<para>TinyOS programs are built out of software components, some of which present hardware abstractions. Components are connected to each other using interfaces. TinyOS provides interfaces and components for common abstractions such as packet communication, routing, sensing, actuation, and storage.</para>
<para>TinyOS cannot be considered a proper real-time OS, since it implements a non-preemptive thread model.</para>
<para>The OS is licensed under BSD license which, like GPL with linking exception, does not require redistribution of the source code of the application.</para>
<para>TinyOS supports Texas Instruments MSP430, Atmel Atmega128, and Intel px27ax families of microcontrollers. Currently, it does not support the family of ARM Cortex processors. The development of TinyOS has been discontinued since a few years.</para>
</section>
<section class="lev2" id="sec7-2-2-10">
<title>ChibiOS/RT</title>
<para>ChibiOS/RT [41] is a compact and Open-Source RTOS. It is designed for embedded real-time applications where execution efficiency and compact code are important requirements. This RTOS is characterized by its high portability, compact size and, mainly, by its architecture optimized for extremely efficient context switching. It supports a preemptive thread model but it does not support stack sharing among threads.</para>
<para>The official list of supported microcontrollers is mainly focused on the ARM Cortex-M family, even if a very few other processors (i.e., ARM7, AVR Mega, MSP430, Power Architecture e200z, and STM8) are supported as well. Some further microcontrollers are not officially supported, and the porting of the RTOSs has been done by individual developers.</para>
<para>The footprint of this RTOS is very low, being between 1 KB and 5.5 KB.</para>
<para>ChibiOS/RT is provided under several licenses. Besides the commercial license, unstable releases are available as GPL v3 and stable releases as GPL v3 with linking exception. Since version 3 of GPL does not allow &#x0201C;tivoization&#x0201D; [42], these Open-Source licenses are not suitable for industrial contexts where the manufacturer wants to prevent users from running modified versions of the software through hardware restrictions.</para>
</section>
<section class="lev2" id="sec7-2-2-11">
<title>ERIKA Enterprise v2</title>
<para>Erika Enterprise v2 [43] is a minimal RTOS providing hard real-time guarantees. It is developed by partner Evidence Srl, but it is released for free. The Open-Source license &#x02013; GPL with linking exception (also known as &#x0201C;Classpath&#x0201D;) [29] &#x02013; is suitable for industrial usage because it allows linking (even statically) the proprietary application code with the RTOS without the need of releasing the source code.</para>
<para>The RTOS was born in 2002 to target the automotive market. During the course of the years it has been certified OSEK/VDX and it is currently used by either automotive companies (as Magneti Marelli and Cobra) or research institutions. ERIKA Enterprise v2 implements the AUTOSAR API 4.0.3 as well, up to Scalability Class 4.</para>
<para>Besides the very small footprint (about 2&#x02013;4 KB), ERIKA Enterprise has innovative features, like advanced scheduling algorithms (e.g., resource reservation, immediate priority ceiling, etc.) and stack sharing to reduce memory usage.</para>
<para>It supports several microcontrollers (from 8-bit to 32-bit) and it has been one of the first RTOSs supporting multicore platforms (i.e., Altera NiosII). The current list of supported architectures includes Atmel AVR and Atmega, ARM 7 and Cortex-M, Altera NiosII, Freescale S12 and MPC, Infineon Aurix and Tricore, Lattice Mico32, Microchip dsPIC and PIC32, Renesas RX200, and TI MSP430. A preliminary support for ARM Cortex-A as well as the integration with Linux on the same multicore chip has been shown during a talk at the Automotive Linux Summit Fall [44] in October 2013.</para>
<para>Version 3 of ERIKA Enterprise has also been released recently [45]. The architecture of ERIKA Enterprise v3 has been directly derived as an evolution of the work described in this chapter, and is aimed to support full AUTOSAR OS compliance on various single and multi-/manycore platforms, including support for hypervisors.</para>
</section>
</section>
<section class="lev2" id="sec7-2-3">
<title>7.2.3 Classification of Embedded RTOSs</title>
<para>The existing open-source RTOSs can be grouped in the following classes:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para>POSIX RTOSs, which provide the typical POSIX API allowing dynamic thread creation and resource allocation. These RTOSs have a large footprint due to the implementation of the powerful but complex POSIX API. Examples are: Marte OS and RTEMS.</para></listitem>
<listitem>
<para>Simil-POSIX RTOSs, which try to offer an API with the same capabilities of POSIX (i.e., dynamic thread creation and resource allocation) but at a lower footprint meeting the typical constraints of small embedded systems. Examples are: FreeRTOS, Ecos and ChibiOS/RT.</para></listitem>
<listitem>
<para>OSEK RTOSs, implementing the OSEK/VDX API with static thread creation but still allowing thread preemption. These RTOSs are characterized by a low footprint. Moreover, they usually also offer stack-sharing among the threads, allowing the reduction of memory consumption at run-time. Examples are: ERIKA Enterprise, Trampoline, and FreeOSEK.</para></listitem>
<listitem>
<para>Other minimal RTOSs, which have a low footprint and a non-preemptive thread model by construction. Usually, these RTOSs offer the stack-sharing capability. Examples are: TinyOS and Contiki.</para></listitem>
</orderedlist>
<para>This classification is shown in the following <link linkend="T7-1">Table <xref linkend="T7-1" remap="7.1"/></link>:</para>
<table-wrap position="float" id="T7-1">
<label><link linkend="T7-1">Table <xref linkend="T7-1" remap="7.1"/></link></label>
<caption><para>Classification of RTOSs</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="bottom" align="left"></td>
<td valign="bottom" align="center">POSIX</td>
<td valign="bottom" align="center">Simil-POSIX</td>
<td valign="bottom" align="center">OSEK</td>
<td valign="bottom" align="center">Other Minimal</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left">API</td>
<td valign="top" align="center">POSIX</td>
<td valign="top" align="center">Custom</td>
<td valign="top" align="center">OSEK/VDX</td>
<td valign="top" align="center">Custom</td>
</tr>
<tr>
<td valign="top" align="left">Footprint size</td>
<td valign="top" align="center">Big</td>
<td valign="top" align="center">Medium</td>
<td valign="top" align="center">Small</td>
<td valign="top" align="center">Small</td>
</tr>
<tr>
<td valign="top" align="left">Thread preemption</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">X</td>
</tr>
<tr>
<td valign="top" align="left">Thread creation</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">&#x02013;</td>
</tr>
<tr>
<td valign="top" align="left">Stack sharing</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">V</td>
<td valign="top" align="center">V</td>
</tr>
<tr>
<td valign="top" align="left">Examples</td>
<td valign="top" align="center">MarteOS RTEMS</td>
<td valign="top" align="center">FreeRTOS Ecos ChibiOS/RT</td>
<td valign="top" align="center">ERIKA Enterprise Trampoline FreeOSEK</td>
<td valign="top" align="center">TinyOS Contiki QP</td></tr>
</tbody>
</table>
</table-wrap>
</section>
</section>
<section class="lev1" id="sec7-3">
<title>7.3 Requirements for The Choice of The Run Time System</title>
<para>This section includes a short description of the main requirements that influenced the choice of the OS platform for the implementation of our parallel programming model.</para>
<section class="lev2" id="sec7-3-1">
<title>7.3.1 Programming Model</title>
<para>The run-time system is a fundamental software component of the parallel programming model to transform the parallel expressions defined by the user into threads that execute in the different processing units, i.e., cores.</para>
<para>Therefore, the OS system must provide support to execute the run-time system that will implement the API services defined by the parallel programming model. In our case, the requirement is related to the fact that an UNIX environment such as Linux should be present, with support for the C and C++ programming languages.</para>
</section>
<section class="lev2" id="sec7-3-2">
<title>7.3.2 Preemption Support</title>
<para>In single-core real-time systems, allowing a thread to be preempted has a positive impact on the schedulability of the system because the blocking on higher-priority jobs is significantly limited. However, in many-core systems, the impact of preemptions on schedulability is not as clear, since higher priority jobs might have a chance to execute on one of the many other cores available in the system. Nevertheless, for highly parallel workloads, it may happen that all cores are occupied by lower-priority parallel jobs, so that higher-priority instances may be blocked for the whole duration of the lower-priority jobs. In this case, a smart preemption support might be beneficial, allowing a subset of the lower-priority instances to be preempted in favor of the higher-priority jobs. The remaining lower-priority instance may continue executing on the remaining cores, while the state of the preempted instances needs to be saved by the OS, in order to restore it as soon as there are computing units available again.</para>
<para>In order to develop the proper OS mechanisms, it is necessary to support the kind of preemption needed by the scheduling algorithms described in <link linkend="ch04">Chapter <xref linkend="ch4" remap="4"/></link>, with particular reference to the hybrid approach known as &#x0201C;limited preemption,&#x0201D; and to the store location of the preempted threads context. In order to implement such techniques, the OS design needs to take into account which restrictions will be imposed on the preemptability of the threads, whether by means of statically defined preemption points, or by postponing the invocation of the scheduling routine by a given amount of time.</para>
</section>
<section class="lev2" id="sec7-3-3">
<title>7.3.3 Migration Support</title>
<para>In migration-based multicore systems, a preempted thread may resume its execution on a different core. Migration support requires additional OS mechanisms to allow threads to be resumed on different cores. Different migration approaches are possible:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>Partitioned approach: Each thread is scheduled on one core and cannot execute on other cores;</para></listitem>
<listitem>
<para>Clustered approach: Each thread can execute on a subset (cluster) of the available cores;</para></listitem>
<listitem>
<para>Global approach: Threads can execute on any of the available cores.</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec7-3-4">
<title>7.3.4 Scheduling Characteristics</title>
<para>Real-time scheduling algorithms are often divided into static vs. dynamic scheduling algorithms, depending on the priority assigned to each job to execute. Static algorithms assign a fixed priority to each thread. Although they are easier to implement, their performance could be lower than with more flexible approaches that may dynamically change priorities of each thread. Depending on the scheduling strategy, fixed or dynamic, different OS kernel mechanisms will be needed.</para>
<para>Another design point concerns the policies for arbitrating the access to mutually exclusive shared resources. Depending on the adopted policy, particular synchronization mechanisms, thread queues, and blocking primitives may be needed.</para>
</section>
<section class="lev2" id="sec7-3-5">
<title>7.3.5 Timing Analysis</title>
<para>In order for the timing analysis tools to be able to compute safe and accurate worst-case timing estimates, it is essential that the RTOS manages all the software/hardware resources in a predictable manner. Also, it is crucial for the timing estimates to be as tight as possible because subsequently these values (like the worst-case execution time of a task or the maximum time to read/write data from/to the main memory) will propagate all the way up and will be used as basic blocks in higher-level analyses like the schedulability analysis. Deriving tight estimates requires that all the OS mechanisms that allocate and arbitrate the access to the system resources are thoroughly documented and do not make use of any procedure that involves randomness or based on non-deterministic parameters.</para>
<para><emphasis role="strong">Task-to-thread and thread-to-core mapping:</emphasis> The allocation of the tasks to the threads and the mapping of the threads to the cores must be documented; ideally, it should also be static and known at design time. If the allocation is dynamic, i.e., computed at run-time, then the allocation/scheduling algorithm should follow a set of deterministic (and fully documented) rules. The knowledge of where and when the tasks execute considerably facilitates the timing analysis process, as it allows for deriving an accurate utilization profile of each resource and then uses those profiles to compute safe bounds on the time it takes to access these resources.</para>
<para><emphasis role="strong">Contract-based resource allocation scheme:</emphasis> Before executing, each application or task has a &#x0201C;contract&#x0201D; with every system resource that it may need to access. Each contract stipulates the minimum share of the system resource (hardware and software) that the task must be allowed to use over time. Considering a communication bus shared between several tasks, a TDMA (Time Division Multiple Access) bus arbitration policy is a good example of a contract-based allocation scheme: the number of time-slots dedicated to each task in a time-frame of fixed length gives the minimum share of the bus that is guaranteed to be granted to the task at run-time. When the resource is a core, contract-based mechanisms are often referred to as reservation-based scheduling. Before executing, an execution budget is assigned to every task and a task can execute on a core only if its allocated budget is not exhausted. Technically speaking, within such reservation-based mechanisms, the scheduling algorithm of the OS does not schedule the execution of the tasks as such, but rather it manages the associated budgets (i.e., empties and replenishes them) and defines the order in which those budgets are granted to the tasks. There are many advantages of using contract-based mechanisms. For example, they provide a simple way of protecting the system against a violation of the timing parameters. If a task fails and starts looping infinitely, for instance, the task will eventually be interrupted once it runs out of budget, without affecting the planned execution of the next tasks. These budgets/contracts can be seen as fault containers. They guarantee a minimum service to every task while enabling the system to identify potential task failure and avoid propagating the potentially harmful consequences of a faulty task through the execution of the other tasks.</para>
<para><emphasis role="strong">Runtime budget/contract reinforcement:</emphasis> Mechanisms must be provided to force the system resources and the tasks to abide with their contract, e.g., a task is not allowed to execute if its CPU budget is exhausted or if its budget is not currently given to that task by the scheduler. This mechanism is known in the real-time literature as &#x0201C;hard reservation.&#x0201D;</para>
<para><emphasis role="strong">Memory isolation:</emphasis> The OS should also provide mechanisms to dedicate regions of the memory to a specific task, or at least to tasks running on a specific core.</para>
<para><emphasis role="strong">Execution independence:</emphasis> The programs on each core shall run independent of the hardware state of other cores.</para>
</section>
</section>
<section class="lev1" id="sec7-4">
<title>7.4 RTOS Selection</title>
<para>Considering the architecture of the reference platform (i.e., host processor connected to a set of accelerators, similarly to other commercially available many-core platforms), we decided to use two different OSs for the host and the many-core processors.</para>
<section class="lev2" id="sec7-4-1">
<title>7.4.1 Host Processor</title>
<para>Linux has been chosen for the host processor, due to its excellent support for peripherals and communication protocols, the several programming models supported, and the popularity in the HPC domain.</para>
<para>Given the nature of the project and the requirements of the use-cases, soft real-time support has been added through the adoption of the PREEMPT_RT patch [10].</para>
</section>
<section class="lev2" id="sec7-4-2">
<title>7.4.2 Manycore Processor</title>
<para>For the manycore processor, a proper RTOS was needed. The selected RTOS should have been Open-Source and lightweight (i.e., with a small footprint) but providing a preemptive thread model. For these reasons, only the RTOSs belonging to columns 2 (i.e., Simil-POSIX) and 3 (i.e., OSEK) of <link linkend="T7-1">Table <xref linkend="T7-1" remap="7.1"/></link> could be selected. Moreover, the selected RTOS must be actively maintained through the support of a development community.</para>
<para>Ecos has been discarded due to the big footprint (comparable to the one of POSIX systems). FreeOSEK has been discarded because the project is not actively maintained and because it does not offer any additional feature with respect to ERIKA Enterprise. ChibiOS/RT, Trampoline, and QP, instead, have been discarded for the too restrictive open-source license, not suitable for industrial products.</para>
<para>The only RTOSs that fulfilled our requirements, therefore, were ERIKA Enterprise [43] and FreeRTOS [28]. The project eventually chose to use ERIKA Enterprise due to its smaller footprint, the availability of advanced real-time features, and the strong know-how in the development team.</para>
</section>
</section>
<section class="lev1" id="sec7-5">
<title>7.5 Operating System Support</title>
<section class="lev2" id="sec7-5-1">
<title>7.5.1 Linux</title>
<para>As for the Linux support, we started with the Linux version provided together with the reference platform. In particular, the Kalray Bostan AccessCore SDK included an Embedded Linux version 3.10, and on top of it we assembled and configured a filesystem based on the Busybox project [46] produced using Buildroot [47].</para>
<para>The Linux version provided included Symmetric Multi-Processing (SMP) support (which is a strong requirement for running PREEMPT_RT [10]), and included the PREEMPT_RT patch.</para>
</section>
<section class="lev2" id="sec7-5-2">
<title>7.5.2 ERIKA Enterprise Support</title>
<para>We have successfully ported the ERIKA Enterprise [43] on the MPPA architecture, supporting its VLIW (Very Large Instruction Word) Instruction Set Architecture (ISA) and implementing the API used by the off-loading mechanism. The following paragraphs list the main challenges we had during the porting, and the main choices we addressed, together with some early performance results.</para>
<section class="lev2" id="sec7-5-2-1">
<title>7.5.2.1 Exokernel support</title>
<para>The development on the platform directly supports the Kalray &#x0201C;exokernel,&#x0201D; which is a set of software, mostly running on the 17th core of each cluster (the resource manager core), used to provide a set of services needed to let a cluster appear &#x0201C;more like&#x0201D; a SMP machine. Among the various services, the exokernel includes communication services and inter-core interrupts. The exokernel API is guaranteed to be maintained across chip releases, while the raw support for the resource manager core will likely change with newer chip releases.</para>
</section>
<section class="lev2" id="sec7-5-2-2">
<title>7.5.2.2 Single-ELF multicore ERIKA Enterprise</title>
<para>One of the main objectives during the porting of the ERIKA RTOS has been the reduction of the memory footprint of the kernel, obtained by using a Single-ELF build system.</para>
<para>The reason is that the multicore support in ERIKA was historically designed for hardware architectures which did not have a uniform memory region, such as Janus [48]. In those architectures, each core had its own local memory and, most importantly, the view of the memory as seen by the various cores was different (that is, the same memory bank was available at a different address on each core). This imposed the need for a custom copy of the RTOS for each core. Other architectures had a uniform memory space, but the visibility of some memory regions was prevented by the Network on Chip. On Altera Nios II, for example, addresses differentiating by only the 31st bit referred to the same physical address with or without caching. This, again, implied the need for separate images (in particular, you can refer to the work done during the FP6 project FRESCOR, D-EP7 [49]). More modern architectures like Freescale PPC and Tricore AURIX allowed the possibility of single-ELF, but the current multi-ELF scaled relatively well on a small number of cores, reducing the need for single-ELF versions of the system.</para>
<para>In manycore architectures such as Kalray, the multi-ELF approach showed its drawback: the high number of cores, in fact, required avoiding code duplication to not waste memory. Moreover, each core has the visibility of a memory region, and the addressing is uniform across the cores. For this reason, after an initial simple single-core port of ERIKA on the Kalray MPPA, the project decided to eventually design a single-ELF implementation; this activity required a complete rewrite of the codebase (named ERIKA Enterprise v3). The new codebase is now in production and sponsored through a dedicated website [45] in order to gather additional comments and feedbacks. The next paragraphs include a short description of the main design guidelines, which are also described in a specific public document [50].</para>
</section>
<section class="lev2" id="sec7-5-2-3">
<title>7.5.2.3 Support for limited preemption, job, and global scheduling</title>
<para>The ERIKA Enterprise RTOS traditionally supported partitioned scheduling, where each core has a set of statically assigned tasks which can be individually activated.</para>
<para>In order to support the features requested by the parallel programming framework, the ERIKA Enterprise scheduler has been modified to allow the following additional features:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para><emphasis role="strong">Limited preemption scheduler</emphasis> &#x02013; ERIKA Enterprise has been improved to allow preemptions only at given instants of time (i.e., at task scheduling points, see Chapters 3 and 6). The main advantage is related to performance, because the preemption is implemented in a moment that has a limited performance hit on the system.</para></listitem>
<listitem>
<para><emphasis role="strong">Job activation</emphasis> &#x02013; In ERIKA Enterprise, each task can be individually activated as the effect of an <emphasis>ActivateTask</emphasis> primitive. In the new environment, the OS tasks are mapped onto the OpenMP worker threads (see <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link>). Those threads are activated in &#x0201C;groups&#x0201D; (named here &#x0201C;jobs&#x0201D;), because their activation is equivalent to the start of an OpenMP offoad composed by N OS tasks on a cluster. For this reason, ERIKA Enterprise now supports &#x0201C;Job activation,&#x0201D; which allows activating a number of tasks on a cluster. Typically, those tasks will have all the same priority (as they map the execution of an OpenMP offoad).</para></listitem>
<listitem>
<para><emphasis role="strong">Global scheduling</emphasis> &#x02013; In order to obtain the maximum throughput, ERIKA implemented a work conserving global scheduler, which is able to implement migration of tasks among cores of the same cluster. The migration support also handles contention on the global queue in case there are two or more cores idle.</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec7-5-2-4">
<title>7.5.2.4 New ERIKA Enterprise primitives</title>
<para>The implementation of ERIKA Enterprise required the creation of a set of <emphasis>ad hoc</emphasis> primitives, which have been included in a new kernel explicitly developed for Kalray. The new primitives are described below:</para>
<para><emphasis role="strong">CreateJob:</emphasis> This primitive is used to create a pool of OS tasks which are coordinated for the parallel execution in a cluster. A &#x0201C;Job&#x0201D; is composed by a maximum number of tasks which is equal to the cluster size (16 on Kalray MPPA). It is possible to specify how many tasks should be created, and on which cores they should be mapped in case of partitioned scheduling. All tasks which are part of a Job have the same priority, the same entry point, the same stack size. Finally, they all have an additional parameter which is used by the OpenMP workers to perform their job.</para>
<para><emphasis role="strong">ReadyJob</emphasis> and <emphasis role="strong">ActivateJob:</emphasis> These two primitives are used to put in the ready queue (either global or partitioned depending on the kernel configuration) the tasks corresponding to a specific mask passed as parameter (the mask is a subset of the one passed previously to CreateJob). In addition to this, ActivateJob adds a preemption point on the calling site and issues inter-core interrupts in full preemptive configuration.</para>
<para><emphasis role="strong">JoinJob:</emphasis> This is a synchronization point at the termination of all tasks of a Job. It must be called on a task which has lower priority than the Job task priority.</para>
<para>Synchronization primitives are also provided to allow the implementation of use-level locks and higher-level programming model synchronization constructs for the OpenMP runtime library (discussed in <link linkend="ch06">Chapter <xref linkend="ch6" remap="6"/></link>).</para>
<para><emphasis role="strong">SpinLockObj</emphasis> and <emphasis role="strong">SpinUnlockObj:</emphasis> These primitives provide a standard lock API, and are directly based on spinlock primitives provided by the Kalray HAL. At the lowest abstraction level, the lock data structure is implemented as a 32-bit integer, which could be allocated at any memory-mapped address. Using this approach, the lock variables can be statically allocated whenever it is possible, and when more dynamism is required, lock data structures can be initialized via standard <literal>malloc</literal> operations on a suitable memory range.</para>
<para><emphasis role="strong">WaitCondition</emphasis> and <emphasis role="strong">SignalValue:</emphasis> These primitives provide a synchronization mechanism based on WAIT/SIGNAL semantics. ERIKA supports four condition operators (equal, not equal, lower than, greater than) and three different wait policies:</para>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><literal>BLOCK_NO</literal> &#x02013; The condition is checked in a busy waiting loop;</para></listitem>
<listitem>
<para><literal>BLOCK_IMMEDIATELY</literal> &#x02013; The condition is checked once. If the check fails (and no other tasks are available for execution) the processor enters sleep mode until the condition is reached. A specific signal is then used to wake-up the processor.</para></listitem>
<listitem>
<para><literal>BLOCK_OS</literal> &#x02013; Informs the OS that the ERIKA task (i.e., the OpenMP thread mapped to that task) is voluntarily yielding the processor. The OS can then use this information to implement different scheduling policies. For example, the task can be suspended and a different task (belonging to a different job) can be scheduled for execution.</para></listitem>
</orderedlist>
</section>
<section class="lev2" id="sec7-5-2-5">
<title>7.5.2.5 New data structures</title>
<para>Addressing the single-ELF image implementation in the end required a restructuring of the kernel data structures.</para>
<para>The initial version of ERIKA Enterprise used a set of global data structures (basically, C arrays of scalars) allocated in RAM or ROM. Each core had its own copy of the data structures, with the same name. Data which is shared among the cores is defined and initialized in one core referred to as the <emphasis>master</emphasis> core. The other cores are called <emphasis>slave</emphasis> cores. Afterwards, when compiling the <emphasis>slave</emphasis> cores&#x02019; code, the locations of the shared data are appended to each core&#x02019;s linker scripts (see also [48]). <link linkend="F7-2">Figure <xref linkend="F7-2" remap="7.2"/></link> shows the structure of the two ELF files, highlighting the first core (<emphasis>master</emphasis>), which has everything defined, and the subsequent <emphasis>slave</emphasis> cores, which have the shared symbols addresses appended in the linker script.</para>
<para>The single-ELF approach required a complete restructuring of the binary image. The complete system is compiled in a single binary image, and the data structures are designed to let the cores access the relevant per-CPU data. The main guidelines used when designing the data structures are the following:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>All data is shared among all cores.</para></listitem>
<listitem>
<para>The code must be able to know on which core it is running. This is done typically using a special register of the architecture that holds the CPU number.</para></listitem>
<listitem>
<para>Given the CPU number, it is possible to access &#x0201C;private&#x0201D; data structures to each core (see <link linkend="F7-3">Figure <xref linkend="F7-3" remap="7.3"/></link>). Note that those &#x0201C;private&#x0201D; data structures can be allocated in special memory regions &#x0201C;near&#x0201D; each core (for example, they could be allocated in sections which can be pinned to per-core caches).</para></listitem>
<listitem>
<para>Clear distinction between Flash Descriptor Blocks (named &#x0002A;DB) and RAM Control Blocks (named &#x0002A;CB). In this way the reader has a clear idea of the kind of content from the name of the data structure.</para></listitem>
<listitem>
<para>Limited usage of pointers (used to point only from Flash to RAM), to make the certification process easier.</para></listitem>
</itemizedlist>
<fig id="F7-2" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F7-2">Figure <xref linkend="F7-2" remap="7.2"/></link></label>
<caption><para>Structure of the multicore images in the original ERIKA Enterprise structure.</para></caption>
<graphic xlink:href="graphics/ch07_fig7_2.jpg"/>
</fig>
<fig id="F7-3" position="float" xmlns:xlink="http://www.w3.org/1999/xlink">
<label><link linkend="F7-3">Figure <xref linkend="F7-3" remap="7.3"/></link></label>
<caption><para>Structure of the Single-ELF image produced by ERIKA Enterprise.</para></caption>
<graphic xlink:href="graphics/ch07_fig7_3.jpg"/>
</fig>
</section>
<section class="lev2" id="sec7-5-2-6">
<title>7.5.2.6 Dynamic task creation</title>
<para>In the original version of ERIKA, RTOS tasks were statically allocated by defining them inside an OIL file. In the new version of ERIKA, we allowed a pre-allocation of a given number of RTOS tasks, which can be afterwards &#x0201C;allocated&#x0201D; using a task creation primitive. In this way, the integration with the upper layers of OpenMP becomes simpler, as OpenMP makes the hypothesis of being able to create as many threads as needed using the underlying Linux primitive <literal>pthread_create</literal>.</para>
<para>In addition to the changes illustrated above, we also took the opportunity for making the following additional changes to ease future developments.</para>
</section>
<section class="lev2" id="sec7-5-2-7">
<title>7.5.2.7 IRQ handlers as tasks</title>
<para>The original version of ERIKA handled interrupts in the most efficient way in the case of no memory protection among tasks. When memory protection comes into play, treating IRQs as special tasks has the advantage of simplifying the codebase.</para>
<para>In view of the future availability of multi-many cores with memory protection we implemented the possibility for an IRQ to be treated as a task. A special fast handler is called upon IRQ arrival, which has the main job of activating the &#x0201C;interrupt task.&#x0201D;</para>
<para>This approach also simplified the codebase by allowing a simpler context change primitive, which in turn simplifies the implementation in VLIW chips such as Kalray.</para>
</section>
<section class="lev2" id="sec7-5-2-8">
<title>7.5.2.8 File hierarchy</title>
<para>For the new version of ERIKA, we adopted a new file hierarchy which aims to a simplification of the codebase. In particular, the main changes of the new codebase are the following:</para>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>In the old version, CPU (the specific instruction set, such as PPC, Cortex-MX, etc.), MCU (the peripherals available on a specific part number), Boards (code related to the connections on the PCB) were stored in directories under the &#x0201C;pkg&#x0201D; directory. With the growing number of architectures supported, this became a limitation which also made the compilation process longer. The new version of the codebase includes MCUs and Boards under the CPU layer, making the dependencies in the codebase clearer.</para></listitem>
<listitem>
<para>We adopted a local self-contained flat (single directory) project structure instead of a complex hierarchy. All needed files are copied once in the project directory at compilation time, leading to simpler makefiles.</para></listitem>
<listitem>
<para>We maintained the RTOS code separated from the Application configuration. This is very useful to allow the deployment of pre-compiled libraries; moreover it allows partial compilation of the code.</para></listitem>
</itemizedlist>
</section>
<section class="lev2" id="sec7-5-2-9">
<title>7.5.2.9 Early performance estimation</title>
<para>Before implementing the Single-ELF version of ERIKA on Kalray, we performed an initial implementation of the traditional single-core porting of ERIKA in order to get a reference for the evaluation of the subsequent development. Please note that the evaluation of the new version of ERIKA has been done on a prototype implementation (not the final one). However, the numbers are good enough to allow a fair comparison of the two solutions.</para>
<para><link linkend="T7-2">Table <xref linkend="T7-2" remap="7.2"/></link> summarizes an early comparison between the old and the new implementation of ERIKA, for a simple application with two tasks on a single core. The purpose of the various columns is the following:</para>
<table-wrap position="float" id="T7-2">
<label><link linkend="T7-2">Table <xref linkend="T7-2" remap="7.2"/></link></label>
<caption><para>ERIKA Enterprise footprint (expressed in bytes)</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="bottom" align="left">Description</td>
<td valign="bottom" align="center">Old Version (&#x0002A;)</td>
<td valign="bottom" align="center">Old Version</td>
<td valign="bottom" align="center">New Version Single-core</td>
<td valign="bottom" align="center">New Version, Multicore with Services for Supporting Libgomp</td>
</tr>
<tr>
<td valign="bottom" align="left" colspan="5"><emphasis role="cline"/></td>
</tr>
<tr>
<td valign="bottom" align="left">Platform</td>
<td valign="bottom" align="center">Nios II</td>
<td valign="bottom" align="center">Kalray MPPA</td>
<td valign="bottom" align="center">Kalray MPPA</td>
<td valign="bottom" align="center">Kalray MPPA</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left">Code footprint (&#x0002A;&#x0002A;)</td>
<td valign="top" align="center">About 800</td>
<td valign="top" align="center">1984</td>
<td valign="top" align="center">2940</td>
<td valign="top" align="center">4156</td>
</tr>
<tr>
<td valign="top" align="left">Code footprint related to multicore (&#x0002A;&#x0002A;&#x0002A;)</td>
<td valign="top" align="center"></td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">4156 + 502 for RM</td>
</tr>
<tr>
<td valign="top" align="left">Flash/Read-only</td>
<td valign="top" align="center">164</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">&#x02013;</td>
<td valign="top" align="center">&#x02013;</td>
</tr>
<tr>
<td valign="top" align="left">RAM</td>
<td valign="top" align="center"></td>
<td valign="top" align="center">192</td>
<td valign="top" align="center">216</td>
<td valign="top" align="center">216 + 128 for each core</td>
</tr>
</tbody>
<table-wrap-foot>
<tr>
<td valign="top" align="left" colspan="5">(&#x0002A;) Numbers taken from D-EP7v2 of the FRESCOR project [49]. These numbers can be taken as a reference for the order of magnitude for the size of the kernel and may not represent the same data structures. We considered these numbers as the current implementation on ERIKA has roughly a similar size and they can be used as a reference for comparing &#x0201C;similar&#x0201D; implementations.</td>
</tr>
<tr>
<td valign="top" align="left" colspan="5">(&#x0002A;&#x0002A;) The code footprint includes the equivalent of the following functions: <emphasis>StartOs</emphasis>, <emphasis>ActivateTask</emphasis>, <emphasis>TerminateTask</emphasis></td>
</tr>
<tr>
<td valign="top" align="left" colspan="5">(&#x0002A;&#x0002A;&#x0002A;) Code related to the handling of the multicore features (remote notifications, inter-processor interrupts, spin locks, and code residing) on the Resource Manager Core (see <link linkend="ch02">Chapter <xref linkend="ch2" remap="2"/></link>).</td></tr>
</table-wrap-foot>
</table>
</table-wrap>
<itemizedlist mark="bullet" spacing="normal">
<listitem>
<para>The comparison between the second and the third column gives a rough idea of the difference in the ISA on a &#x0201C;reasonably similar&#x0201D; code on another (different) architecture, Nios II.</para></listitem>
<listitem>
<para>The comparison between the third and the fourth column gives a rough idea of the impact of the changes of the new version of ERIKA over the old version. The values show an increase of the code footprint. This increase, however, is less than indicated by the table: the old version of ERIKA, in fact, does contain the support for multiple task activations (which has not been compiled) and dynamic task creation (which was not available). Moreover, we have to consider that the old version of ERIKA needed 1,984 bytes for each core. The new version of ERIKA, instead, needs 2,940 bytes, regardless of the number of cores. This means that with just two cores, the amount of memory needed by the new version of ERIKA Enterprise is less than using the old version of the RTOS.</para></listitem>
<listitem>
<para>The comparison between the fourth and the fifth column gives a rough idea of the impact of the multicore support. The increase of the code footprint is mainly due to additional synchronization primitives (i.e., spinlocks) needed for distributed scheduling &#x02013; i.e., to allow the &#x0201C;group activation&#x0201D; done by the Resource Manager on behalf of OpenMP. Therefore, this increase is specific to the Kalray architecture, and it is missing on other (e.g., shared-memory) architectures. Note that the footprint takes into account only the kernel part with the services for supporting the OpenMP runtime library; it does not include the library itself. <footnote id="fn7_1" label="1"> <para>The footprint takes into account only kernel and support for the OpenMP runtime library; it does not include the library itself.</para></footnote> <footnote id="fn7_2" label="2"> <para>128 = 44 (core data structures) + 84 (idle task).</para></footnote></para></listitem>
</itemizedlist>
<para><link linkend="T7-3">Table <xref linkend="T7-3" remap="7.3"/></link> provides basic measurements of activation and pre-emption of tasks on a single-core:</para>
<table-wrap position="float" id="T7-3">
<label><link linkend="T7-3">Table <xref linkend="T7-3" remap="7.3"/></link></label>
<caption><para>Timings (expressed in clock ticks)</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="top" align="left">Feature</td>
<td valign="top" align="center">Time on ERIKA</td></tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left"><emphasis>ActivateTask</emphasis>, no preemption</td>
<td valign="top" align="center">384</td>
</tr>
<tr>
<td valign="top" align="left"><emphasis>ActivateTask</emphasis>, preemption</td>
<td valign="top" align="center">622</td>
</tr>
<tr>
<td valign="top" align="left">An IRQ happens, no preemption</td>
<td valign="top" align="center">585</td>
</tr>
<tr>
<td valign="top" align="left">An IRQ happens, with preemption</td>
<td valign="top" align="center">866</td></tr>
</tbody>
</table>
</table-wrap>
<para>Tables 7.4 and 7.5 provide some timing references to compare ERIKA Enterprise (which is a RTOS) with NodeOS on MPPA-256, taken using the Kalray MPPA tracer. Since NodeOS does not support preemption (and therefore a core can execute only one thread) we have configured ERIKA Enterprise to run only one task on each core as well. Then, we have measured footprint and execution times. In particular, <link linkend="T7-4">Table <xref linkend="T7-4" remap="7.4"/></link> provides a rough comparison of the footprint for ERIKA and NodeOS on Kalray MPPA. For ERIKA, the footprint also takes into account the per-core and per-task data structures in a cluster composed of 17 cores. This footprint can be reduced by using a static configuration of the RTOS. <link linkend="T7-5">Table <xref linkend="T7-5" remap="7.5"/></link> provides a comparison between the thread creation time on NodeOS and the equivalent inter-core task activation on ERIKA.</para>
<table-wrap position="float" id="T7-4">
<label><link linkend="T7-4">Table <xref linkend="T7-4" remap="7.4"/></link></label>
<caption><para>Footprint comparison between ERIKA and NodeOS for a 16-core cluster (expressed in bytes)</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="bottom" align="left">Description</td>
<td valign="bottom" align="center">ERIKA New Version, Multicore with Services for Supporting OpenMP</td>
<td valign="bottom" align="center">NodeOS</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left">Code footprint</td>
<td valign="top" align="center">4484<superscript>3</superscript></td>
<td valign="top" align="center">10060</td>
</tr>
<tr>
<td valign="top" align="left">RAM</td>
<td valign="top" align="center">2184</td>
<td valign="top" align="center">2196</td></tr>
</tbody>
</table>
</table-wrap>
<table-wrap position="float" id="T7-5">
<label><link linkend="T7-5">Table <xref linkend="T7-5" remap="7.5"/></link></label>
<caption><para>Thread creation/activation times (expressed in clock ticks)</para></caption>
<table cellspacing="5" cellpadding="5" frame="hsides" rules="groups">
<thead>
<tr>
<td valign="bottom" align="center">Inter-core Task Activation on ERIKA</td>
<td valign="bottom" align="center">Thread Creation on NodeOS</td>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="center">1200</td>
<td valign="top" align="center">3300</td></tr>
</tbody>
</table>
</table-wrap>
<para><footnote id="fn7_3" label="3"> <para>The footprint takes into account only kernel and support for libgomp; it does not include the whole libgomp library.</para></footnote></para>
</section>
</section></section>
<section class="lev1" id="sec7-6">
<title>7.6 Summary</title>
<para>This chapter illustrated the state of the art of the OSs suitable for the reference parallel programming model. After reviewing the main requirements that influenced the implementation, the selection of the RTOS for the reference platform has been described for both the host processor and the manycore accelerators. Furthermore, a description of the main implementation choices for the ERIKA Enterprise v3 and Linux OS have been detailed. As can be seen, the result of the implementation provides a complete system which is capable of addressing high-performance workloads thanks to the synergies between the general-purpose OS Linux and the ERIKA Enterprise RTOS.</para>
</section>
<section class="lev1" id="sec7-7">
<title>References</title>
<orderedlist numeration="arabic" continuation="restarts" spacing="normal">
<listitem>
<para><emphasis>Top500, Linux OS</emphasis>. Available at: <ulink url="http://www.top500.org/statistics/details/osfam/1">http://www.top500.org/statistics/details/ osfam/1</ulink></para></listitem>
<listitem>
<para><emphasis>RCU</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/Read-copy-update">http://en.wikipedia.org/wiki/Read-copy-update</ulink></para></listitem>
<listitem>
<para><emphasis>GNU General Public License (GPL)</emphasis>, available at: <ulink url="https://www.gnu.org/copyleft/gpl.html">https://www.gnu.org/ copyleft/gpl.html</ulink></para></listitem>
<listitem>
<para>Lipari, G., Scordino, C., <emphasis>Linux and Real-Time: Current Approaches and Future Opportunities, International Congress ANIPLA</emphasis>, Rome, Italy, 2006. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lipari%2C+G%2E%2C+Scordino%2C+C%2E%2C+Linux+and+Real-Time%3A+Current+Approaches+and+Future+Opportunities%2C+International+Congress+ANIPLA%2C+Rome%2C+Italy%2C+2006%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>RTLinux</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/RTLinux">http://en.wikipedia.org/wiki/RTLinux</ulink></para></listitem>
<listitem>
<para><emphasis>RTAI</emphasis> &#x02013; <emphasis>the RealTime Application Interface for Linux</emphasis>, available at: <ulink url="https://www.rtai.org/">https://www.rtai.org/</ulink></para></listitem>
<listitem>
<para>Xenomai, <emphasis>Real-Time Framework for Linux</emphasis>. Available at: <ulink url="http://www.xenomai.org/">http://www. xenomai.org/</ulink></para></listitem>
<listitem>
<para>Windriver, <emphasis>VxWorks RTOS</emphasis>. Available at: <ulink url="http://www.windriver.com/products/vxworks/">h</ulink>ttp://www.windriver.com/ products/vxworks/</para></listitem>
<listitem>
<para><emphasis>POSIX IEEE standard</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/POSIX">h</ulink>ttp://en.wikipedia. org/wiki/POSIX</para></listitem>
<listitem>
<para><emphasis>The Real Time Linux project</emphasis>, available at: <ulink url="https://wiki.linuxfoundation.org/realtime/start">https://wiki.linuxfoundation. org/realtime/start</ulink></para></listitem>
<listitem>
<para><emphasis>The Linux Foundation</emphasis>, available at: <ulink url="https://www.linuxfoundation.org/">https://www.linuxfoundation.org/</ulink></para></listitem>
<listitem>
<para>Libenzi, D., <emphasis>SCHED SOFTRR Linux Scheduler Policy</emphasis>, available at: <ulink url="http://xmailserver.org/linux-patches/softrr.html">http://xmailserver.org/linux-patches/softrr.html</ulink></para></listitem>
<listitem>
<para>Kolivas, C., <emphasis>Isochronous class for unprivileged soft RT scheduling</emphasis>. Available at: <ulink url="http://ck.kolivas.org/patches/">http://ck.kolivas.org/patches/</ulink></para></listitem>
<listitem>
<para><emphasis>SCHED_DEADLINE Linux Patch</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/SCHED_DEADLINE">http://en.wikipedia.org/ wiki/SCHED_DEADLINE</ulink></para></listitem>
<listitem>
<para>Lelli, J., Scordino, C., Abeni, L., Faggioli, D., Deadline scheduling in the Linux kernel, Software: Practice and Experience, 46, pp. 821&#x02013;839, 2016. <ulink url="https://scholar.google.com/scholar?hl=en&amp;q=Lelli%2C+J%2E%2C+Scordino%2C+C%2E%2C+Abeni%2C+L%2E%2C+Faggioli%2C+D%2E%2C+Deadline+scheduling+in+the+Linux+kernel%2C+Software%3A+Practice+and+Experience%2C+46%2C+pp%2E+821-839%2C+2016%2E" target="_blank">Google Scholar</ulink></para></listitem>
<listitem>
<para><emphasis>ACTORS European Project</emphasis>, available at: <ulink url="http://www.actors-project.eu/">http://www.actors-project.eu/</ulink></para></listitem>
<listitem>
<para><emphasis>Earliest Deadline First (EDF)</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling">http://en.wikipedia.org/wiki/ Earliest_deadline_first_scheduling</ulink></para></listitem>
<listitem>
<para>Green Hills, <emphasis>Integrity RTOS</emphasis>. Available at: <ulink url="http://www.ghs.com/products/rtos/integrity.html">http://www.ghs.com/products/rtos/integrity.html</ulink></para></listitem>
<listitem>
<para><emphasis>QNX RTOS</emphasis>, available at: <ulink url="http://www.qnx.com/">http://www.qnx.com/</ulink></para></listitem>
<listitem>
<para><emphasis>SYSGO PikeOS</emphasis>, available at: <ulink url="http://www.sysgo.com/products/pikeos-rtos-and-virtualization-concept/">http://www.sysgo.com/products/pikeos-rtos-and-virtualization-concept/</ulink></para></listitem>
<listitem>
<para>Mentor Graphics, <emphasis>Nucleus RTOS</emphasis>. Available at: <ulink url="http://www.mentor.com/embedded-software/nucleus/">http://www.mentor.com/embedded-software/nucleus/</ulink></para></listitem>
<listitem>
<para><emphasis>LynuxWorks LynxOS</emphasis>, available at: <ulink url="http://www.lynuxworks.com/rtos/">http://www.lynuxworks.com/rtos/</ulink></para></listitem>
<listitem>
<para><emphasis>Micrium &#x003BC;c/OS-III</emphasis>, available at: <ulink url="http://micrium.com/">http://micrium.com/</ulink></para></listitem>
<listitem>
<para><emphasis>Segger EmbOS</emphasis>, available at: <ulink url="http://www.segger.com/embos.html">http://www.segger.com/embos.html</ulink></para></listitem>
<listitem>
<para><emphasis>ENEA OSE</emphasis>, available at: <ulink url="http://www.enea.com/ose">http://www.enea.com/ose</ulink></para></listitem>
<listitem>
<para><emphasis>Arccore Arctic Core</emphasis>, available at: <ulink url="http://www.arccore.com/products/">http://www.arccore.com/products/</ulink></para></listitem>
<listitem>
<para>Wikipedia, <emphasis>List of Real-Time Operating Systems</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/List_of_real-time_operating_systems">http:// en.wikipedia.org/wiki/List_of_real-time_operating_systems</ulink></para></listitem>
<listitem>
<para><emphasis>FreeRTOS</emphasis>, available at: <ulink url="http://www.freertos.org/">http://www.freertos.org/</ulink></para></listitem>
<listitem>
<para><emphasis>GPL Linking Exception</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/GPL_linking_exception">http://en.wikipedia.org/wiki/GPL_linking_exception</ulink></para></listitem>
<listitem>
<para><emphasis>Contiki</emphasis>, available at: <ulink url="http://www.contiki-os.org/">http://www.contiki-os.org/</ulink></para></listitem>
<listitem>
<para>Wikipedia, <emphasis>Protothreads</emphasis>. Available at: <ulink url="http://en.wikipedia.org/wiki/Protothreads">http://en.wikipedia.org/wiki/ Protothreads</ulink></para></listitem>
<listitem>
<para><emphasis>Marte OS</emphasis>, available at: <ulink url="http://marte.unican.es/">http://marte.unican.es/</ulink></para></listitem>
<listitem>
<para><emphasis>Ecos RTOS</emphasis>, available at: <ulink url="http://ecos.sourceware.org/">http://ecos.sourceware.org/</ulink></para></listitem>
<listitem>
<para><emphasis>FreeOSEK RTOS</emphasis>, available at: <ulink url="http://opensek.sourceforge.net/">http://opensek.sourceforge.net/</ulink></para></listitem>
<listitem>
<para>Quantum Leaps, <emphasis>QP<superscript>TM</superscript> Active Object Frameworks for Embedded Systems</emphasis>, available at: <ulink url="http://www.state-machine.com/">http://www.state-machine.com/</ulink></para></listitem>
<listitem>
<para><emphasis>Trampoline RTOS</emphasis>, available at: <ulink url="http://trampoline.rts-software.org/">http://trampoline.rts-software.org/</ulink></para></listitem>
<listitem>
<para><emphasis>RTEMS</emphasis>, available at: <ulink url="http://www.rtems.org/">http://www.rtems.org/</ulink></para></listitem>
<listitem>
<para><emphasis>Footprint of RTEMS</emphasis>, available at: <ulink url="http://www.rtems.org/ml/rtems-users/2004/september/msg00188.html">http://www.rtems.org/ml/rtems-users/2004/september/msg00188.html</ulink></para></listitem>
<listitem>
<para><emphasis>Tiny RTEMS</emphasis>, available at: <ulink url="https://code.google.com/p/tiny-rtems/">https://code.google.com/p/tiny-rtems/</ulink></para></listitem>
<listitem>
<para><emphasis>TinyOS</emphasis>, available at: <ulink url="http://www.tinyos.net/">http://www.tinyos.net/</ulink></para></listitem>
<listitem>
<para><emphasis>ChibiOS/RT</emphasis>, available at: <ulink url="http://www.chibios.org/">http://www.chibios.org/</ulink></para></listitem>
<listitem>
<para><emphasis>Tivoization</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/Tivoization">http://en.wikipedia.org/wiki/Tivoization</ulink></para></listitem>
<listitem>
<para><emphasis>Erika Enterprise RTOS</emphasis>, available at: <ulink url="http://erika.tuxfamily.org">http://erika.tuxfamily.org</ulink></para></listitem>
<listitem>
<para><emphasis>Automotive Linux Summit Fall</emphasis>, available at: <ulink url="http://en.wikipedia.org/wiki/Protothreads">http://events.linuxfounda tion.org/events/automotive-linux-summit-fall</ulink></para></listitem>
<listitem>
<para><emphasis>ERIKA Enterprise v3</emphasis>, available at: <ulink url="http://www.erika-enterprise.com">http://www.erika-enterprise.com</ulink></para></listitem>
<listitem>
<para><emphasis>Busybox</emphasis>, available at <ulink url="http://www.busybox.net/">http://www.busybox.net/</ulink>, last accessed March 2016.</para></listitem>
<listitem>
<para><emphasis>Buildroot</emphasis>, available at <ulink url="http://buildroot.uclibc.org/">http://buildroot.uclibc.org/</ulink>, last accessed March 2016.</para></listitem>
<listitem>
<para>Ferrari, A., Garue, S., Peri, M., Pezzini, S., Valsecchi, L., Andretta, F., and Nesci, W., &#x0201C;The design and implementation of a dual-core platform for power-train systems.&#x0201D; In <emphasis>Convergence 2000</emphasis>, Detroit, MI, USA, 2000.</para></listitem>
<listitem>
<para><emphasis>FRESCOR FP6 D-EP7v2</emphasis>, available at <ulink url="http://www.frescor.org/">http://www.frescor.org/</ulink> and also <ulink url="http://erika.tuxfamily.org/wiki/index.php?title=Altera_Nios_II">http://www.frescor.org/</ulink> and also <ulink url="http://erika.tuxfamily.org/wiki/index.php?title=Altera_Nios_II">http://erika.tuxfamily.org/wiki/index.php?title=Altera_Nios_II</ulink>, last accessed March 2016.</para></listitem>
<listitem>
<para>Evidence, <emphasis>ERIKA Enterprise Version 3 Requirement Document</emphasis>, available at ERIKA Enterprise website: <ulink url="http://erika.tuxfamily.org/drupal/content/erika-enterprise-3">http://erika.tuxfamily.org/drupal/content/erika-enterprise-3</ulink></para></listitem>
</orderedlist>
</section>
</chapter>
<chapter class="nosec" id="ch08">
<title>About the Editors</title>
<para><emphasis role="strong">Lu&#x000ED;s Miguel Pinho</emphasis> is Professor at the Department of Computer Engineering of the School of Engineering, Polytechnic Institute of Porto, Portugal, with a PhD in Electrical and Computer Engineering at the University of Porto, Portugal. He has more than 20 years of experience in research in the area of real-time and embedded systems, particularly in concurrent and parallel programming models, languages, and runtime systems. He is Research Associate in the CISTER research unit, where he was Vice-Director from 2010 to 2017, being responsible for creating several research areas and topics, among which the activities on parallel real-time systems, that he leads. He has participated in more than 20 R&#x00026;D projects, was Project Coordinator and Technical Manager of the FP7 R&#x00026;D European Project P-SOCRATES and national-funded CooperatES and Reflect Projects. He was also coordinator of the participation of CISTER and work package leader in several other international and national projects. He has published more than 100 papers in international conferences and journals in the area of real-time embedded systems. He was Senior Researcher of the ArtistDesign NoE and is currently a member of the HiPEAC NoE. He was Keynote Speaker at the 16th IEEE Conference on Embedded and Real-Time Computing Systems and Applications (RTCSA 2010) and is the Editor-in-Chief of the Ada User Journal. Among others, he was General Co-Chair of the 28th GI/ITG International Conference on Architecture of Computing Systems (ARCS 2015), and Program Co-Chair of the 24th International Conference on Real-Time Networks and Systems (RTNS 2016) and of the 21st International Conference on Reliable Software Technologies (Ada-Europe 2016).</para> 
<para><emphasis role="strong">Eduardo Qui&#x000F1;ones</emphasis> is a senior researcher in the group on Interaction between the Computer Architecture and the Operating System (CAOS) at BSC and member of HIPEAC. He worked at the Intel Barcelona Research Center from 2002 till 2004 in compiler techniques for EPIC architectures (including Itanium I and II). At BSC, he has previous experiences involved in the architectural definition and the avionics case study definition in the MERASA FP7 project and he leads the architectural definition work packages of the PROARTIS and the parMERASA FP7 projects, and lead the applicability of HPC parallel programming models to real-time embedded systems to increase performance in the P-SOCRATES FP7 project. Moreover, he is involved in two research projects with the European Space Agency (ESA), one as a technical manager. His research area focuses on compiler techniques and many-core architectures for safety-critical systems on which he is co-advising six PhD students. He is currently the project coordinator for the CLASS H2020 project.</para>
<para><emphasis role="strong">Marko Bertogna</emphasis> is Associate Professor at the University of Modena (Italy), where he leads the High-Perfomance Real-Time Systems Laboratory (HiPeRT Lab). His main research interests are in High-Performance Real-Time systems, especially based on multi- and many-core devices, Autonomous Driving and Industrial Automation systems, with particular relation to related timing and safety requirements. Previously, he was Assistant Professor at the Scuola Superiore Sant&#x02019;Anna of Pisa, working at the Real-Time Systems Lab since 2003. He graduated magna cum laude in Telecommunication Engineering at the University of Bologna in 2002. From 2001 to 2002, he worked on integrated optical devices at the Technical University of Delft, The Netherlands. In 2006, he visited the University of North Carolina at Chapel Hill, working with prof. Sanjoy Baruah on scheduling algorithms for single and multicore real-time systems. In 2008, he received a PhD in Computer Sciences from the Scuola Superiore Sant&#x02019;Anna of Pisa, with a dissertation on Real-Time Systems for Multicore Platforms, awarded as the best scientific PhD thesis discussed at Scuola Superiore Sant&#x02019;Anna in 2008 and 2009.</para>
<para><emphasis role="strong">Andrea Marongiu</emphasis> received the PhD degree in electronic engineering from the University of Bologna, Italy, in 2010. He has been a postdoctoral reserch fellow at ETH Zurich, Switzerland. He currently holds an assistant professor position at the University of Bologna (Department of Computer Science and Engineering). His research interests focus on programming models and architectures in the domain of heterogeneous multi- and many-core systems on a chip. This includes language, compiler, runtime and architecture support to efficiently address performance, predictability, energy and reliability issues in paralle, embedded systems, as well as HW-SW co-design of accelerator-based MPSoCs. In this field, he has published more than 100 papers in international peer-reviewed conferences and journals, with more than 700 citations and an h-index of 16 [Google Scholar]. He has collaborated with several international research institutes and companies.</para>
<para><emphasis role="strong">Vincent N&#x000E9;lis</emphasis> earned his PhD degree in Computer Science at the University of Brussels (ULB) in 2010. Since then, he has been working at CISTER as a Research Associate. He is an expert in real-time scheduling theory with a focus on multiprocessor/multicore systems and in interference analysis, including pre-emption cost analysis and bus/network contention analysis in multicores and many-cores systems. Vincent is regularly a member of technical program committees for international conferences, workshops, and journals. He has graduated 2 PhDs and he is currently the supervisor of a third PhD student. He has contributed to 5 R&#x00026;D projects and published 25+ papers with about 30 different co-authors in international conferences and scientific journals. His work was awarded at several occasions: &#x0201C;Solvay Award&#x0201D; (2006), &#x0201C;Outstanding Paper Award&#x0201D; (2012), two &#x0201C;Best Paper Awards&#x0201D; (2010 and 2013) and a &#x0201C;Best Presentation Award&#x0201D; (2013).</para>
<para><emphasis role="strong">Paolo Gai</emphasis> graduated (cum laude) in Computer Engineering at University of Pisa in 2000. He obtained the PhD from Scuola Superiore Sant&#x02019;Anna in 2004. Since 2002 he is founder of Evidence Srl, a company providing innovations in the field of operating systems and platforms for embedded devices in the automotive and industrial fields.</para>
<para>His research activity is focused on the development of hard real-time architectures for embedded automotive control systems. His research interests include multi and many-core processor systems, object-oriented programming, real-time operating systems, scheduling algorithms, multimedia applications, and hypervisors.</para>
<para><emphasis role="strong">Juan Sancho</emphasis> holds a degree in Telecommunications Engineering from the Universidad Polit&#x000E9;cnica de Valencia, Spain. He developed his Final Project Degree in the field of Health Monitoring using Wireless Sensor Networks at the Wireless Centre of the Copenhagen University of Engineering, Denmark. In the past he worked as network &#x00026; systems engineer, participating in several European FP7, ENIAC and National projects (BUTLER, TOISE, SICRA, TSMART). Since 2014 he works as a Research &#x00026; Innovation Engineer in ATOS Research &#x00026; Innovation division, collaborating in FP7 and H2020 projects related to IoT topics (COSMOS, P-SOCRATES) and the Energy domain (inteGRIDy, ELVITEN, eDREAM). His research interests cover Big Data &#x00026; Edge platforms, DevOps, Renewable Energy Sources, WSN and low-power embedded systems.</para>
</chapter>
</book>
