<h1>Related Work<aclass="headerlink"href="#related-work"title="Permalink to this headline">¶</a></h1>
<p>At first sight, Triton may seem like just yet another DSL for DNNs. The purpose of this section is to contextualize Triton and highlights its differences with the two leading approaches in this domain: polyhedral compilation and scheduling languages.</p>
<divclass="section"id="polyhedral-compilation">
<h2>Polyhedral Compilation<aclass="headerlink"href="#polyhedral-compilation"title="Permalink to this headline">¶</a></h2>
<p>Traditional compilers typically rely on intermediate representations, such as LLVM-IR <aclass="reference internal"href="#lattner2004"id="id1"><span>[LATTNER2004]</span></a>, that encode control flow information using (un)conditional branches. This relatively low-level format makes it difficult to statically analyze the runtime behavior (e.g., cache misses) of input programs, and to automatically optimize loops accordingly through the use of tiling <aclass="reference internal"href="#wolfe1989"id="id2"><span>[WOLFE1989]</span></a>, fusion <aclass="reference internal"href="#darte1999"id="id3"><span>[DARTE1999]</span></a> and interchange <aclass="reference internal"href="#allen1984"id="id4"><span>[ALLEN1984]</span></a>. To solve this issue, polyhedral compilers <aclass="reference internal"href="#ancourt1991"id="id5"><span>[ANCOURT1991]</span></a> rely on program representations that have statically predictable control flow, thereby enabling aggressive compile-time program transformations for data locality and parallelism. Though this strategy has been adopted by many languages and compilers for DNNs such as Tiramisu <aclass="reference internal"href="#baghdadi2021"id="id6"><span>[BAGHDADI2021]</span></a>, Tensor Comprehensions <aclass="reference internal"href="#vasilache2018"id="id7"><span>[VASILACHE2018]</span></a>, Diesel <aclass="reference internal"href="#elango2018"id="id8"><span>[ELANGO2018]</span></a> and the Affine dialect in MLIR <aclass="reference internal"href="#lattner2019"id="id9"><span>[LATTNER2019]</span></a>, it also comes with a number of limitations that will be described later in this section.</p>
<h3>Program Representation<aclass="headerlink"href="#program-representation"title="Permalink to this headline">¶</a></h3>
<p>Polyhedral compilation is a vast area of research. In this section we only outline the most basic aspects of this topic, but readers interested in the solid mathematical foundations underneath may refer to the ample litterature on linear and integer programming.</p>
<p>Polyhedral compilers focus on a class of programs commonly known as <strong>Static Control Parts</strong> (SCoP), <em>i.e.</em>, maximal sets of consecutive statements in which conditionals and loop bounds are affine functions of surrounding loop indices and global invariant parameters. As shown above, programs in this format always lead to iteration domains that are bounded by affine inequalities, i.e., polyhedral. These polyhedra can also be defined algebraically; for the above example:</p>
<divclass="math notranslate nohighlight">
\[\begin{split}\mathcal{P} = \{ i, j \in \mathbb{Z}^2
~|~
\begin{pmatrix}
1 & 0 \\
-1 & 0 \\
-1 & 1 \\
0 & -1 \\
\end{pmatrix}
\begin{pmatrix}
i \\
j
\end{pmatrix}
+
\begin{pmatrix}
0 \\
2 \\
0 \\
4
\end{pmatrix}
\geq
0
\}\end{split}\]</div>
<p>Each point <spanclass="math notranslate nohighlight">\((i, j)\)</span> in <spanclass="math notranslate nohighlight">\(\mathcal{P}\)</span> represents a <em>polyhedral statement</em>, that is a program statement which (1) does not induce control-flow side effects (e.g., <codeclass="code docutils literal notranslate"><spanclass="pre">for</span></code>, <codeclass="code docutils literal notranslate"><spanclass="pre">if</span></code>, <codeclass="code docutils literal notranslate"><spanclass="pre">break</span></code>) and (2) contains only affine functions of loop indices and global parameters in array accesses. To facilitate alias analysis, array accesses are also mathematically abstracted, using so-called <em>access function</em>. In other words, <codeclass="code docutils literal notranslate"><spanclass="pre">A[i][j]</span></code> is simply <codeclass="code docutils literal notranslate"><spanclass="pre">A[f(i,j)]</span></code> where the access function <spanclass="math notranslate nohighlight">\(f\)</span> is defined by:</p>
<divclass="math notranslate nohighlight">
\[\begin{split}f(i, j) = \begin{pmatrix}
1 & 0\\
0 & 1\\
\end{pmatrix}
\begin{pmatrix}
i\\
j
\end{pmatrix}
=
(i, j)\end{split}\]</div>
<p>Note that the iteration domains of an SCoP does not specify the order in which its statements shall execute. In fact, this iteration domain may be traversed in many different possible legal orders, i.e. <em>schedules</em>. Formally, a schedule is defined as a p-dimensional affine transformation <spanclass="math notranslate nohighlight">\(\Theta\)</span> of loop indices <spanclass="math notranslate nohighlight">\(\mathbf{x}\)</span> and global invariant parameters <spanclass="math notranslate nohighlight">\(\mathbf{g}\)</span>:</p>
<p>Where <spanclass="math notranslate nohighlight">\(\Theta_S(\mathbf{x})\)</span> is a p-dimensional vector representing the slowest to fastest growing indices (from left to right) when traversing the loop nest surrounding <spanclass="math notranslate nohighlight">\(S\)</span>. For the code shown above, the original schedule defined by the loop nest in C can be retrieved by using:</p>
<p>where <spanclass="math notranslate nohighlight">\(i\)</span> and <spanclass="math notranslate nohighlight">\(j\)</span> are respectively the slowest and fastest growing loop indices in the nest. If <spanclass="math notranslate nohighlight">\(T_S\)</span> is a vector (resp. tensor), then <spanclass="math notranslate nohighlight">\(\Theta_S\)</span> is a said to be one-dimensional (resp. multi-dimensional).</p>
</div>
<divclass="section"id="advantages">
<h3>Advantages<aclass="headerlink"href="#advantages"title="Permalink to this headline">¶</a></h3>
<p>Programs amenable to polyhedral compilation can be aggressively transformed and optimized. Most of these transformations actually boil down to the production of schedules and iteration domains that enable loop transformations promoting parallelism and spatial/temporal data locality (e.g., fusion, interchange, tiling, parallelization).</p>
<p>Polyhedral compilers can also automatically go through complex verification processes to ensure that the semantics of their input program is preserved throughout this optimization phase. Note that polyhedral optimizers are not incompatible with more standard optimization techniques. In fact, it is not uncommon for these systems to be implemented as a set of LLVM passes that can be run ahead of more traditional compilation techniques <aclass="reference internal"href="#grosser2012"id="id10"><span>[GROSSER2012]</span></a>.</p>
<p>All in all, polyhedral machinery is extremely powerful, when applicable. It has been shown to support most common loop transformations, and has indeed achieved performance comparable to state-of-the-art GPU libraries for dense matrix multiplication <aclass="reference internal"href="#elango2018"id="id11"><span>[ELANGO2018]</span></a>. Additionally, it is also fully automatic and doesn’t require any hint from programmers apart from source-code in a C-like format.</p>
<h3>Limitations<aclass="headerlink"href="#limitations"title="Permalink to this headline">¶</a></h3>
<p>Unfortunately, polyhedral compilers suffer from two major limitations that have prevented its adoption as a universal method for code generation in neural networks.</p>
<p>First, the set of possible program transformations $Omega = { Theta_S ~|~ S in text{program} }$ is large, and grows with the number of statements in the program as well as with the size of their iteration domain. Verifying the legality of each transformation can also require the resolution of complex integer linear programs, making polyhedral compilation very computationally expensive. To make matters worse, hardware properties (e.g., cache size, number of SMs) and contextual characteristics (e.g., input tensor shapes) also have to be taken into account by this framework, leading to expensive auto-tuning procedures <aclass="reference internal"href="#sato2019"id="id12"><span>[SATO2019]</span></a>.</p>
<p>Second, the polyhedral framework is not very generally applicable; SCoPs are relatively common <aclass="reference internal"href="#girbal2006"id="id13"><span>[GIRBAL2006]</span></a> but require loop bounds and array subscripts to be affine functions of loop indices, which typically only occurs in regular, dense computations. For this reason, this framework still has to be successfully applied to sparse – or even structured-sparse – neural networks, whose importance has been rapidly rising over the past few years.</p>
<p>On the other hand, blocked program representations advocated by this dissertation are less restricted in scope and can achieve close to peak performance using standard dataflow analysis.</p>
</div>
</div>
<divclass="section"id="scheduling-languages">
<h2>Scheduling Languages<aclass="headerlink"href="#scheduling-languages"title="Permalink to this headline">¶</a></h2>
<p>Separation of concerns cite{dijkstra82} is a well-known design principle in computer science: programs should be decomposed into modular layers of abstraction that separate the semantics of their algorithms from the details of their implementation. Systems like Halide and TVM push this philosophy one step further, and enforce this separation at the grammatical level through the use of a <strong>scheduling language</strong>. The benefits of this methodology are particularly visible in the case of matrix multiplication, where, as one can see below, the definition of the algorithm (Line 1-7) is completely disjoint from its implementation (Line 8-16), meaning that both can be maintained, optimized and distributed independently.</p>
<p>The resulting code may however not be completely portable, as schedules can sometimes rely on execution models (e.g., SPMD) or hardware intrinsics (e.g., matrix-multiply-accumulate) that are not widely available. This issue can be mitigated by auto-scheduling mechanisms <aclass="reference internal"href="#mullapudi2016"id="id14"><span>[MULLAPUDI2016]</span></a>.</p>
<h3>Advantages<aclass="headerlink"href="#id15"title="Permalink to this headline">¶</a></h3>
<p>The main advantage of this approach is that it allows programmers to write an algorithm <em>only once</em>, and focus on performance optimization separately. It makes it possible to manually specify optimizations that a polyhedral compiler wouldn’t be able to figure out automatically using static data-flow analysis.</p>
<p>Scheduling languages are, without a doubt, one of the most popular approaches for neural network code generation. The most popular system for this purpose is probably TVM, which provides good performance across a wide range of platforms as well as built-in automatic scheduling mechanisms.</p>
</div>
<divclass="section"id="id16">
<h3>Limitations<aclass="headerlink"href="#id16"title="Permalink to this headline">¶</a></h3>
<p>This ease-of-development comes at a cost. First of all, existing systems that follow this paradigm tend to be noticeably slower than Triton on modern hardware when applicable (e.g., V100/A100 tensor cores w/ equal tile sizes). I do believe that this is not a fundamental issue of scheduling languages – in the sense that it could probably be solved with more efforts – but it could mean that these systems are harder to engineer. More importantly, existing scheduling languages generate loops whose bounds and increments cannot depend on surrounding loop indice without at least imposing severe constraints on possible schedules – if not breaking the system entirely. This is problematic for sparse com-putations, whose iteration spaces may be irregular.</p>
<p>On the other hand, the block-based program representation that we advocate for through this work allows for block-structured iteration spaces and allows programmers to manually handle load-balancing as they wish.</p>
</div>
</div>
<divclass="section"id="references">
<h2>References<aclass="headerlink"href="#references"title="Permalink to this headline">¶</a></h2>
<li><p>Girbal et al., “Semi-Automatic Composition of Loop Transformations for Deep Parallelism and Memory Hierarchies”, International Journal of Parallel Programming 2006</p></li>