From be514142da7ed91e9f034d4d3b512a90bae8e04f Mon Sep 17 00:00:00 2001 From: cianciosa Date: Fri, 6 Feb 2026 20:49:53 -0500 Subject: [PATCH] Fix errors found in the benchmarking and fix several issues with documentation. --- graph_docs/Comparison.png | Bin 32471 -> 49151 bytes graph_docs/code_performance.dox | 62 ++++++++++++++++++++++++++---- graph_docs/discription.dox | 17 ++++---- graph_docs/general.dox | 17 ++++---- graph_docs/tutorial.dox | 38 +++++++++--------- graph_docs/use_cases.dox | 12 +++--- graph_framework/cpu_context.hpp | 2 +- graph_framework/cuda_context.hpp | 2 +- graph_framework/metal_context.hpp | 2 +- graph_framework/node.hpp | 2 +- graph_framework/workflow.hpp | 8 +++- 11 files changed, 108 insertions(+), 54 deletions(-) diff --git a/graph_docs/Comparison.png b/graph_docs/Comparison.png index af33ffbff9ca889039a2f6d3897d648ef611f17b..293d5769b03acd3311744eea871c3943a960aa90 100644 GIT binary patch literal 49151 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 Tuzv;v0|SGntDnm{r-UW|vJniV literal 32471 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 VeVgxk&sWgZ>7K5BF6*2UngF!GyHNlD diff --git a/graph_docs/code_performance.dox b/graph_docs/code_performance.dox index 444574a..ac08f5b 100644 --- a/graph_docs/code_performance.dox +++ b/graph_docs/code_performance.dox @@ -45,12 +45,16 @@ * @f{equation}{\frac{\partial\vec{v}}{\partial t} = dt\vec{v}\times\vec{B}@f} * @f{equation}{\frac{\partial\vec{x}}{\partial t} = dt\vec{v}@f} * - * We compared the graph framework against the MLX framework since it supports - * Apple GPUs and JAX due to it's popularity. Source codes for this benchmark - * case is available in the appendix. Figure \ref{fig:compare} shows the through put of - * pushing $10^{8}$ particles for $10^{3}$ time steps. The graph framework - * consistently shows the best throughput on both CPUs and GPUs. Note MLX CPU - * throughput could by improved by splitting the problem to multiple threads. + * We compared the graph framework against the + * MLX + * framework since it supports Apple GPUs, + * JAX due to it's popularity, + * and Kokkos for its performance + * portability. Source codes for this benchmark case is available in the + * appendix. Figure \ref{fig:compare} shows the through put of pushing $10^{8}$ + * particles for $10^{3}$ time steps. The graph framework consistently shows the + * best throughput on both CPUs and GPUs. Note MLX CPU throughput could by + * improved by splitting the problem to multiple threads. * * @subsection code_performance_comparison_codes Source codes for throughput benchmark comparison * @subsubsection code_performance_comparison_graph Graph Framework @@ -93,7 +97,7 @@ for (size_t i = 0, ie = threads.size(); i < ie; i++) { auto v_next = v + dt*lorentz; auto pos_next = pos + dt*v_next; - workflow::manager work(0); + workflow::manager work(thread_number); work.add_item({ graph::variable_cast(x), graph::variable_cast(y), @@ -177,7 +181,7 @@ const auto total_time = end - start; def push(x, y, z, vx, vy, vz): dt = 0.000001 vx_next = vx + dt*(vy*1 - vz*0) - vy_next = vy + dt*(vz*0 - vy*1) + vy_next = vy + dt*(vz*0 - vx*1) vz_next = vz + dt*(vx*0 - vy*0) return vx_next, vy_next, vz_next, x + dt*vx_next, y + dt*vy_next, z + dt*vz_next @@ -201,6 +205,48 @@ jax.block_until_ready([x, y, z, vx, vy, vz]) end = time.time() print(end - start) + @endcode + * + * @subsubsection code_performance_comparison_kokkos Kokkos + * @code +const size_t size = 100000000; +const size_t steps = 1000; + +using ViewVectorType = Kokkos::View; +ViewVectorType x("x", size); +ViewVectorType y("y", size); +ViewVectorType z("z", size); + +ViewVectorType vx("vx", size); +ViewVectorType vy("vy", size); +ViewVectorType vz("vz", size); + +Kokkos::parallel_for(size, KOKKOS_LAMBDA(const int64_t index) { + vx[index] = 1; + vz[index] = 1; +}); + +const std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + +for (size_t i = 0; i < steps; i++) { + Kokkos::parallel_for(size, KOKKOS_LAMBDA(const int64_t index) { + const float dt = 0.000001; + const float vx_next = vx[index] + dt*(vy[index]*1 - vz[index]*0); + const float vy_next = vy[index] + dt*(vz[index]*0 - vx[index]*1); + const float vz_next = vz[index] + dt*(vx[index]*0 - vy[index]*0); + x[index] += dt*vx_next; + y[index] += dt*vy_next; + z[index] += dt*vz_next; + vx[index] = vx_next; + vy[index] = vy_next; + vz[index] = vz_next; + }); +} + +Kokkos::fence(); + +std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); +const auto total_time = end - start; @endcode */ diff --git a/graph_docs/discription.dox b/graph_docs/discription.dox index 6cee615..b5017a1 100644 --- a/graph_docs/discription.dox +++ b/graph_docs/discription.dox @@ -5,8 +5,8 @@ * @section discription_introduction Introduction * The basic functionality of this framework is to build expression graphs * representing mathematical equations. Reduce those graphs to simpler forms. - * Transform those graph to take derivatives. Just-In-Time (JIT) compile them to - * available compute device kernels. Then run those kernels in workflow. The + * Transform those graphs to take derivatives. Just-In-Time (JIT) compile them + * to available compute device kernels. Then run those kernels in workflows. The * code is written in using C++23 features. To simplify embedding into legacy * codes, there are additional language bindings for C and Fortran. * @@ -48,9 +48,10 @@ * be reduced to a single constant by calling the evaluate method. Sub-graph * expressions are combined, factored out, or moved to enable better reductions * on subsequent passes. As new ways of reducing the graph are implemented, - * current and existing code built using this framework benefit from improved - * speed. The figure above shows a visualization of the tree data structure for - * the equation of a line, the derivative, and the subsequent reductions. + * current and existing code built using this framework will benefit from + * improved speed. The figure above shows a visualization of the tree data + * structure for the equation of a line, the derivative, and the subsequent + * reductions. * * @subsubsection discription_graphs_builds Building Graphs * As an example building an expression of line @f$y=mx+b@f$ accomplished by @@ -79,8 +80,8 @@ auto dydmx = y->df(0.5*x); * running them in order. One @ref workflow::manager is created for each device * or thread. The user is responsible for creating threads. Each kernel is * generated through a @ref workflow::work_item. A work item is defined by - * kernel @ref graph::input_nodes, @ref graph::output_nodes and - * @ref graph::map_nodes. Map items are used to take the results of kernel and + * kernel @ref graph::input_nodes, @ref graph::output_nodes, and + * @ref graph::map_nodes. Map items are used to take the results of a kernel and * update an input buffer. Using our example of line equation, we can create a * workflow to compute @f$y@f$ and @f$\frac{\partial y}{\partial x}@f$. * @code @@ -99,7 +100,7 @@ work.add_item({ * elements in the inputs. Multiple work items can be created and will be * executed in order of creation. * - * Once the work items are defined that can be JIT compiled to a backend device. + * Once the work items are defined they can be JIT compiled to a backend device. * The graph framework supports back ends for generic CPUs, Apple Metal GPUs, * Nvidia Cuda GPUs, and initial HIP support of AMD GPUs. Each back end supplies * relevant driver code to build the kernel source, compile the kernel, build diff --git a/graph_docs/general.dox b/graph_docs/general.dox index 40b41f5..0034b55 100644 --- a/graph_docs/general.dox +++ b/graph_docs/general.dox @@ -39,7 +39,7 @@ * as either variables @f$x@f$ or constants @f$m,b@f$. These nodes are connected * by nodes for multiply and addition operations. The output @f$y@f$ represents * the entire graph of operations. - * @image{} html line_graph.png "The graph structure for @f$y=mx+b@f$." + * @image{} html line_graph.png "The graph structure for y = mx + b." * Evaluation of graphs start from the top most node in this case the @f$+@f$ * operation. Evaluation of a node is not performed until all sub-nodes are * evaluated starting with the left operand. Evaluation starts by recursively @@ -58,9 +58,10 @@ * graphs of a function derivative. For an example of taking derivatives see the * @ref tutorial_derivatives "auto differentiation tutorial". Lets say that we * want to take the derivative of @f$\frac{\partial y}{\partial x}@f$. This is - * achieved by evaluating the until bottom left most node is reached. Then a new - * graph is build starting with @f$\frac{\partial m}{\partial x}=0@f$. Applying - * the first half of the chain rule we build a new graph for @f$0x@f$ + * achieved by evaluating the graph until the bottom left most node is reached. + * Then a new graph is constucted starting with + * @f$\frac{\partial m}{\partial x}=0@f$. Applying the first half of the chain + * rule we build a new graph for @f$0x@f$ * @image{} html line_graph_dydf1.png "" * Then we take the derivative of the right operand and apply the second half * of the chain rule to build a new graph for @f$0x=0@f$. @@ -73,8 +74,8 @@ * The final expression for @f$\frac{\partial y}{\partial x}@f$ contains many * unnecessary nodes in the graph. Instead of building full graphs, we can * simplify and eliminate nodes as we build them. For instance, when the - * expression @f$0x@f$ this created can be immediately reduce it to a single - * node. + * expression @f$0\times x@f$ is created, this can be immediately reduced to a + * single node @f$0@f$. * @image{} html line_graph_reduce1.png "" * Applying all possible reductions reduces the final expression to * @f$\frac{\partial y}{\partial x}=m@f$. @@ -109,7 +110,7 @@ * @subsection general_concepts_compile_maps Maps * Maps enable the results of an output node to be stored in an input node. This * is used for a wide varity of cases. For instance take a gradient decent step. - * @f{equation}{y = y + \frac{\partial f}{\partial x}@f} + * @f{equation}{y_{i+1} = y_{i} + \frac{\partial f}{\partial x}@f} * In this case the output of the expression * @f$y + \frac{\partial f}{\partial x}@f$ * can be mapped to update @f$y@f$. @@ -122,7 +123,7 @@ *
* @section general_concepts_safe_math Safe Math * There are some conditions where mathematically, a graph should evaluate to a - * normal number. However, when evaluated suing floating point precision, can + * normal number. However, when evaluated using floating point precision, can * lead to Inf or NaN. An example of this the * @f$\exp\left(x\right)@f$ function. For large argument values, * @f$\exp\left(x\right)@f$ overflows the maximum floating point precision and diff --git a/graph_docs/tutorial.dox b/graph_docs/tutorial.dox index a59329f..d3aaca4 100644 --- a/graph_docs/tutorial.dox +++ b/graph_docs/tutorial.dox @@ -13,7 +13,7 @@ * executable target which can be used to test out the API's of this framework. * The playground starts with a blank main function. * @code -#include "../graph_framework/jit.hpp" +#include "graph_framework.hpp" int main(int argc, const char * argv[]) { START_GPU @@ -30,7 +30,7 @@ int main(int argc, const char * argv[]) { * main. This will allow us to play with different floating point types. For now * we will start with a simple float type. * @code -#include "../graph_framework/jit.hpp" +#include "graph_framework.hpp" template void run_tutorial() { @@ -84,16 +84,16 @@ void run_tutorial() { * so all method are called using the -> operator. * * @subsection tutorial_constant Constant Nodes - * Next we want to define a constant. There are two method to define constants + * Next we want to define a constant. There are two methods to define constants * explicitly or implicitly. * @code template void run_tutorial() { auto x = graph::variable(1000, "x"); -// Define explicit constant. +// Define explicit constant. auto m = graph::constant (0.4); -// Define implicit constant. +// Define implicit constant. const T b = 0.6; } @endcode @@ -110,9 +110,9 @@ template void run_tutorial() { auto x = graph::variable(1000, "x"); -// Define explicit constant. +// Define explicit constant. auto m = graph::constant (0.4); -// Define implicit constant. +// Define implicit constant. const T b = 0.6; // Equation of a line @@ -133,15 +133,15 @@ template void run_tutorial() { auto x = graph::variable(1000, "x"); -// Define explicit constant. +// Define explicit constant. auto m = graph::constant (0.4); -// Define implicit constant. +// Define implicit constant. const T b = 0.6; -// Equation of a line +// Equation of a line auto y = m*x + b; -// Auto differentiation. +// Auto differentiation. auto dydx = y->df(x); dydx->to_latex(); std::cout << std::endl; @@ -168,18 +168,18 @@ template void run_tutorial() { auto x = graph::variable(3, "x"); -// Define explicit constant. +// Define explicit constant. auto m = graph::constant (0.4); -// Define implicit constant. +// Define implicit constant. const T b = 0.6; -// Equation of a line +// Equation of a line auto y = m*x + b; -// Auto differentiation. +// Auto differentiation. auto dydx = y->df(x); -// Create a workflow manager. +// Create a workflow manager. workflow::manager work(0); } @endcode @@ -322,13 +322,13 @@ void run_tutorial() { auto x = graph::variable (3, "x"); x->set({1.0, 2.0, 3.0}); -// Define an objective function. +// Define an objective function. auto f = 0.2*x*x*x + 0.6*x*x + 0.4*x + 0.5; // Define a step update. auto x_new = x - f/f->df(x); -// Create a workflow manager. +// Create a workflow manager. workflow::manager work(0); work.add_item({ graph::variable_cast(x) @@ -372,7 +372,7 @@ void run_tutorial() { * a reduction on the host side and transferring the entire array to the host. * To improve this we can use a converge item instead. * @code -// Create a workflow manager. +// Create a workflow manager. workflow::manager work(0); work.add_converge_item({ graph::variable_cast(x) diff --git a/graph_docs/use_cases.dox b/graph_docs/use_cases.dox index a2e7f53..f47fa27 100644 --- a/graph_docs/use_cases.dox +++ b/graph_docs/use_cases.dox @@ -11,9 +11,9 @@ * @subsection use_cases_rf RF Ray tracing * Geometric optics is a set of asymptotic approximation methods to solve wave * equations. The physics of the particular wave determines an algebraic - * relation between $\omega$ and $\vec{k}$ called a dispersion relation, - * @f$D\left(\omega,\vec{k}\right)=0@f$. Since the parameter $t$ does not appear - * explicitly in the dispersion relation, the function + * relation between @f$\omega@f$ and @f$\vec{k}@f$ called a dispersion relation, + * @f$D\left(\omega,\vec{k}\right)=0@f$. Since the parameter @f$t@f$ does not + * appear explicitly in the dispersion relation, the function * @f$\omega\left(\vec{k}\left(t\right),\vec{x}\left(t\right)\right)@f$ is * constant along the ray trajectory * @f{equation}{\frac{\partial\omega}{\partial t}=\frac{\partial\omega}{\partial\vec{x}}\cdot\frac{\partial\vec{x}}{\partial t}+\frac{\partial\omega}{\partial\vec{k}}\cdot\frac{\partial\vec{k}}{\partial t}\equiv 0@f} @@ -41,7 +41,7 @@ * by relatively simple dispersion relations in plane stratified plasmas, that * is plasma with spatial variation only in the @f$x@f$ direction. In a * spatially varying medium, at a given frequency, there may be regions in which - * the solution of the dispersion relation, $\vec{k}$, is real, and the wave + * the solution of the dispersion relation, @f$\vec{k}@f$, is real, and the wave * propagates. In other regions @f$\vec{k}@f$ is imaginary and the wave does not * propagate, referred to as evanescent. The boundary between a region of * propagation and evanescence is a surface called a cut-off. It is also @@ -53,11 +53,11 @@ * behavior, and the behavior of rays in their vicinity is an indication of the * correctness of the solution. * - * For plasma, the spatial dependence of the dispersion relation comes through + * For plasmas, the spatial dependence of the dispersion relation comes through * variation of the plasma equilibrium quantities. These include the vector * magnetic field, @f$\vec{B}\left(x\right)@f$, the density of each plasma * particle species, @f$n_{s}\left(x\right)@f$, and the temperature of each - * particle species, @f$T_{s}\left(x\right)@f$, where $s$ indicates a + * particle species, @f$T_{s}\left(x\right)@f$, where @f$s@f$ indicates a * particular species. For the cases presented here a linear gradient along the * @f$x@f$ direction is taken for either the particle density or magnetic field * strength. diff --git a/graph_framework/cpu_context.hpp b/graph_framework/cpu_context.hpp index f7d6775..9e27c9d 100644 --- a/graph_framework/cpu_context.hpp +++ b/graph_framework/cpu_context.hpp @@ -116,7 +116,7 @@ namespace gpu { //------------------------------------------------------------------------------ /// @brief Construct a cpu context. /// -/// @param[in] index Concurrent index. Not used. +/// @param[in] index Device index. Not used. //------------------------------------------------------------------------------ cpu_context(const size_t index) { llvm::InitializeNativeTarget(); diff --git a/graph_framework/cuda_context.hpp b/graph_framework/cuda_context.hpp index ee02e0b..dc660a0 100644 --- a/graph_framework/cuda_context.hpp +++ b/graph_framework/cuda_context.hpp @@ -134,7 +134,7 @@ namespace gpu { //------------------------------------------------------------------------------ /// @brief Cuda context constructor. /// -/// @param[in] index Concurrent index. +/// @param[in] index Device index. //------------------------------------------------------------------------------ cuda_context(const size_t index) : result_buffer(0), module(0), offset_buffer(0) { check_error(cuDeviceGet(&device, index), "cuDeviceGet"); diff --git a/graph_framework/metal_context.hpp b/graph_framework/metal_context.hpp index ae78312..5e0bc96 100644 --- a/graph_framework/metal_context.hpp +++ b/graph_framework/metal_context.hpp @@ -65,7 +65,7 @@ namespace gpu { //------------------------------------------------------------------------------ /// @brief Construct a metal context. /// -/// @param[in] index Concurrent index. +/// @param[in] index Device index. //------------------------------------------------------------------------------ metal_context(const size_t index) : device([MTLCopyAllDevices() objectAtIndex:index]), diff --git a/graph_framework/node.hpp b/graph_framework/node.hpp index ae729bd..9e675db 100644 --- a/graph_framework/node.hpp +++ b/graph_framework/node.hpp @@ -311,7 +311,7 @@ /// @code /// virtual shared_leaf remove_pseudo() { /// if (this->has_pseudo()) { -/// return sqrt(this->arg->remove_pseudo()); +/// return foo(this->arg->remove_pseudo()); /// } /// return this->shared_from_this(); /// } diff --git a/graph_framework/workflow.hpp b/graph_framework/workflow.hpp index 421442b..012892c 100644 --- a/graph_framework/workflow.hpp +++ b/graph_framework/workflow.hpp @@ -183,7 +183,13 @@ namespace workflow { //------------------------------------------------------------------------------ /// @brief Workflow manager constructor. /// -/// @param[in] index Concurrent index. +/// For GPU devices, this select the device number to run on. For CPU devices +/// this parameter is ignored. +/// +/// @note It is possible to create multiple workflow managers for the same +/// GPU device and may have performance benefits todo so. +/// +/// @param[in] index Device index. //------------------------------------------------------------------------------ manager(const size_t index) : context(index), add_reduction(false) {} -- GitLab