implementation: started describing frontend
Some checks failed
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.10) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.6) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, pre) (push) Has been cancelled

This commit is contained in:
Daniel 2025-04-21 11:58:48 +02:00
parent 210831146a
commit b40a06af3f
7 changed files with 279 additions and 1 deletions

View File

@ -0,0 +1,190 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0" version="26.2.13">
<diagram name="Page-1" id="lU6yIZpM7DUpZBHmU8TQ">
<mxGraphModel dx="985" dy="546" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="At30AJG1-aIKQqd058rT-89" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="40" y="240" width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-90" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-91" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="40" as="sourcePoint" />
<mxPoint x="400" y="40" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-92" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="79.57999999999998" as="sourcePoint" />
<mxPoint x="400" y="79.57999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-93" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="120" as="sourcePoint" />
<mxPoint x="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-94" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="120" as="sourcePoint" />
<mxPoint x="319.58" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-95" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="120" as="sourcePoint" />
<mxPoint x="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-96" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="240" y="120" as="sourcePoint" />
<mxPoint x="240" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-97" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="120" as="sourcePoint" />
<mxPoint x="200" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-98" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="160" y="120" as="sourcePoint" />
<mxPoint x="160" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-99" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="120" as="sourcePoint" />
<mxPoint x="120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-100" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="120" as="sourcePoint" />
<mxPoint x="80" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-101" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-89">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="40" y="120" as="sourcePoint" />
<mxPoint x="40" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-88" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="40" y="80" width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-16" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-24" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="40" as="sourcePoint" />
<mxPoint x="400" y="40" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-25" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="79.57999999999998" as="sourcePoint" />
<mxPoint x="400" y="79.57999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-31" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="120" as="sourcePoint" />
<mxPoint x="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-64" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="120" as="sourcePoint" />
<mxPoint x="319.58" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-65" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="120" as="sourcePoint" />
<mxPoint x="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-66" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="240" y="120" as="sourcePoint" />
<mxPoint x="240" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-67" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="120" as="sourcePoint" />
<mxPoint x="200" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-68" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="160" y="120" as="sourcePoint" />
<mxPoint x="160" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-69" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="120" as="sourcePoint" />
<mxPoint x="120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-70" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="120" as="sourcePoint" />
<mxPoint x="80" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-71" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="At30AJG1-aIKQqd058rT-88">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="40" y="120" as="sourcePoint" />
<mxPoint x="40" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-54" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="40" y="240" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-55" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="200" y="320" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-56" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="280" y="240" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-60" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#00CC00;strokeWidth=3;" vertex="1" parent="1">
<mxGeometry x="195" y="315" width="170" height="50" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-51" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="40" y="80" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-52" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="80" y="80" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-53" value="Elem" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="120" y="80" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-58" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#00CC00;strokeWidth=3;" vertex="1" parent="1">
<mxGeometry x="35" y="75" width="170" height="50" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-57" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#00CC00;strokeWidth=3;" vertex="1" parent="1">
<mxGeometry x="35" y="235" width="170" height="50" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-59" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#00CC00;strokeWidth=3;" vertex="1" parent="1">
<mxGeometry x="275" y="235" width="170" height="50" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-62" value="&lt;b&gt;Array of Elements:&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="35" y="48" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="At30AJG1-aIKQqd058rT-63" value="&lt;b&gt;Array of Pointers:&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="35" y="208" width="105" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

49
other/expr_ast.drawio Normal file
View File

@ -0,0 +1,49 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0" version="26.2.9">
<diagram name="Page-1" id="6PRo98IcIigsbWnrE1av">
<mxGraphModel dx="2595" dy="1618" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Lucida Console;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-21" target="Z0Q_i6cja5BoQgUPI0nb-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-21" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;log&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;" vertex="1" parent="1">
<mxGeometry x="-556" y="-550" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-22" target="Z0Q_i6cja5BoQgUPI0nb-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-22" value="&lt;font face=&quot;Lucida Console&quot;&gt;1 + x&lt;sub&gt;1&lt;/sub&gt; * log(p&lt;sub&gt;1&lt;/sub&gt;)&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="-730" y="-760" width="140" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-23" value="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;fontSize=15;" vertex="1" parent="1">
<mxGeometry x="-800" y="-620" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-25" value="x&lt;sub&gt;1&lt;/sub&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;fontSize=15;" vertex="1" parent="1">
<mxGeometry x="-700" y="-550" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-26" value="p&lt;sub&gt;1&lt;/sub&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Lucida Console;fontSize=15;" vertex="1" parent="1">
<mxGeometry x="-556" y="-480" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-28" target="Z0Q_i6cja5BoQgUPI0nb-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-28" target="Z0Q_i6cja5BoQgUPI0nb-36">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-28" value="&lt;font face=&quot;Lucida Console&quot;&gt;+&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-710" y="-690" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-36" target="Z0Q_i6cja5BoQgUPI0nb-25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z0Q_i6cja5BoQgUPI0nb-36" target="Z0Q_i6cja5BoQgUPI0nb-21">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Z0Q_i6cja5BoQgUPI0nb-36" value="&lt;font face=&quot;Lucida Console&quot;&gt;*&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-626" y="-620" width="100" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -55,6 +55,7 @@ Based on the requirements and data structure above, the architecture of both pro
A design decision that has been made for both prototypes is to split the evaluation of each expression into a separate kernel or kernel dispatch as seen in Figure \ref{fig:kernel_architecture}. As explained in Section \ref{sec:thread_hierarchy}, it is desirable to reduce the occurrence of thread divergence as much as possible. Although the SIMT programming model tries to mitigate the negative effects of thread divergence, it is still a good idea to avoid it when possible. For this use-case, thread divergence can easily be avoided by not evaluating all expressions in a single kernel or kernel dispatch. GPUs are able to have multiple resident grids, with modern GPUs being able to accommodate 128 grids concurrently \parencite{nvidia_cuda_2025}. One grid corresponds to one kernel dispatch, and therefore allows up-to 128 kernels to be run concurrently. Therefore, dispatching a kernel for each expression, further increases GPU utilisation. In the case of the interpreter, having only one kernel that can be dispatched for each expression, also simplifies the kernel itself. This is because the kernel can focus on evaluating one expression and does not require additional code to handle multiple expressions at once. Similarly, the transpiler can also be simplified, as it can generate many smaller kernels rather than one big kernel. Additionally, the smaller kernels do not need any branching, because the generated code only needs to perform the operations as they occur in the expression itself. This also reduces the overhead on the GPU.
\subsection{Pre-Processing}
\label{sec:pre-processing}
The first step in both prototypes is the pre-processing step. It is needed, as it simplifies working with the expressions in the later steps. One of the responsibilities of the pre-processor is to verify that only allowed operators and symbols are present in the given expressions. This is comparable to the work a scanner like Flex\footnote{\url{https://github.com/westes/flex}} performs. Secondly, this step also converts the expression into an intermediate representation. In essence, the pre-processing step can be compared to the frontend of a compiler as described in Section \ref{sec:compilers}. If new operators are required, the pre-processor must be extended as well. Otherwise, expressions containing these operators would be treated as invalid and never reach the evaluator.
The conversion into the intermediate representation transforms the expressions from infix-notation into postfix notation. This further allows the later parts to more easily evaluate the expressions. One of the major benefits of this notation is the implicit operator precedence. It allows the evaluators to evaluate the expressions token by token from left to right, without needing to worry about the correct order of operations. One token represents either an operator, a constant value, a variable or a parameter. Apart from the intermediate representation containing the expression in postfix notation, it also contains information about the types of the tokens themselves. This is all that is needed for the interpretation and transpilation steps. A simple expression like $x + 2$ would look like depicted in figure \ref{fig:pre-processing_results} after the pre-processing step.

View File

@ -26,7 +26,45 @@ Additionally, the JuliaGPU initiative\footnote{\url{https://juliagpu.org/}} offe
Again, the question arises if the performance of CUDA.jl is sufficient to be used as an alternative to C++ and CUDA. Performance studies by \textcite{besard_rapid_2019}, \textcite{lin_comparing_2021} and \textcite{faingnaert_flexible_2022} have demonstrated, that CUDA.jl provides sufficient performance. They found that in some cases CUDA.jl was able to perform better than the same algorithm implemented in C and C++. This provides the confidence, that Julia alongside CUDA.jl is a good choice for leveraging the performance of GPUs to speed-up expression evaluation.
\section{Pre-Processing}
Talk about why this needs to be done and how it is done (the why is basically: simplifies evaluation/transpilation process; the how is in ExpressionProcessing.jl (the why is probably not needed because it is explained in concept and design))
% Talk about why this needs to be done and how it is done (the why is basically: simplifies evaluation/transpilation process; the how is in ExpressionProcessing.jl (the why is probably not needed because it is explained in concept and design))
The pre-processing or frontend step is very important. As already explained in Chapter \ref{cha:conceptdesign} it is responsible for ensuring the given expressions are valid and that they are transformed into an intermediate representation. This section aims at explaining how the intermediate representation is implemented, as well as how it is generated from a mathematical expression.
\subsection{Intermediate Representation}
% Talk about how it looks and why it was chosen to look like this
The intermediate representation is mainly designed to be lightweight and easily transferrable to the GPU. Since the interpreter is running on the GPU this was a very important consideration. Since the transpilation process is performed on the CPU and is therefore very flexible in terms of the intermediate representation, the focus lied mostly on being efficient for the interpreter.
The intermediate representation can not take on any form. While it has already been defined that expressions are converted to postfix notation, there are different ways of storing the data. The first logical decision is to create an array where each entry represents a token. On the CPU it would be possible to define each entry to be a pointer to the token object. Each of these objects could be of a different type, for example an object holding a constant value while another object holds an operator. Additionally, each of these objects could include its own logic on what to do when it is encountered during the evaluation process. However, on the GPU, this is not possible, as an array entry must hold a value and not a pointer to another memory location. Furthermore, if this would be possible, it would be a bad idea. As explained in Section \ref{sec:memory_model}, when loading data from memory, larger chunks are retrieved at once. If the data is scattered around the GPUs memory, a lot of unwanted data is transferred. This can be seen in figure \ref{fig:excessive-memory-transfer}, where if the data is stored consecutive, much fewer data operations and much less data in general needs to be transferred.
% I think this would more belong to concept and design tbh
\begin{figure}
\centering
\includegraphics[width=.9\textwidth]{excessive_memory_transfer.png}
\caption{Loading data from global memory on the GPU always loads 32, 64 or 128 bytes (see Section \ref{sec:memory_model}). If pointers were supported and data would be scattered around global memory, many more data load operations would be required. Additionally, much more unwanted data would be loaded.}
\label{fig:excessive-memory-transfer}
\end{figure}
Because of this and because the GPU does not allow pointers, another solution is required. Instead of storing pointers to objects of different types in an array, it is possible to store one object with meta information. The object therefore contains the type of the value stored, and the value itself as described in \ref{sec:pre-processing}.
% Talk about variables, operators and constants (floats) very different types. Solution -> vars/params Integers as index (TODO: probably change so that instead of type index, have type param and type var?); operators as op_codes (integers) -> reinterpret constants from float to int; now everything can be sent in single object with type information and value
\subsection{Expressions} % Probably just part of section "Processing" and not its own? Or maybe subsection of "Processing"
With the pre-processing step the first modern feature of Julia has been used. As already mentioned, Julia offers extensive support for meta-programming. Julia represents its own code as a data structure, which allows a developer to manipulate the code at runtime. The code is stored in the so-called Expr object as an abstract syntax tree (AST). As a result, mathematical expressions can also be represented as such an Expr object instead of a simple string. This has the major benefit, that these expression can then easily be manipulated by the symbolic regression algorithm, which is the reason why the pre-processing step requires the expressions to be provided as an Expr object and not as a string.
Another major benefit of the expressions being stored in the Expr object and therefore as an AST, is the included operator precedence. Because it is a tree where the leaves are the constants, variables or parameters (also called terminal symbols) and the nodes are the operators, the correct result will be calculated when evaluating the tree from bottom to top. As seen in Figure \ref{fig:expr-ast} the expression $1 + x_1 \, \log(p_1)$ when parsed as an AST contains the correct operator precedence. First the bottom most subtree $\log(p_1)$ must be evaluated before the multiplication and after that the addition can be evaluated.
\begin{figure}
\centering
\includegraphics[width=.8\textwidth]{expr_ast.png}
\caption{An AST as generated by Julia. Some additional details Julia includes in its AST have been omitted as they are not relevant.}
\label{fig:expr-ast}
\end{figure}
\subsection{Processing}
% because exprs are stored as AST -> from bottom to top create postfix
% while conversion -> invalid symbols cannot be parsed -> invalid expr -> stop
Because all expressions are already stored as an AST,
\section{Interpreter}
Talk about how the interpreter has been developed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
thesis/images/expr_ast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.