=Paper= {{Paper |id=Vol-3867/paper2 |storemode=property |title=Evaluating CUDA-Aware Approximate Computing Techniques |pdfUrl=https://ceur-ws.org/Vol-3867/paper2.pdf |volume=Vol-3867 |authors=Işıl Öz |dblpUrl=https://dblp.org/rec/conf/raw2/Oz24 }} ==Evaluating CUDA-Aware Approximate Computing Techniques== https://ceur-ws.org/Vol-3867/paper2.pdf
                         Evaluating CUDA-Aware Approximate Computing Techniques
                         Işıl Öz1
                         1
                             Computer Engineering Department, Izmir Institute of Technology, Izmir, Turkey


                                             Abstract
                                             Approximate computing techniques offer performance improvements by performing inexact computations. Moreover, CUDA programs
                                             written to be executed on GPU devices employ specific features to utilize the parallel computation units of heterogeneous GPU
                                             architectures. While generic software-level approximate computing techniques have been applied to heterogeneous CUDA programs,
                                             CUDA-specific approaches may introduce promising performance improvements by not corrupting the target computations. In this work,
                                             we propose software approximation techniques for CUDA programs: kernel-aware loop perforation, partition-level synchronization,
                                             block-level atomic operations, and warp divergence elimination. We perform source code transformations on target benchmark
                                             programs by applying our techniques. We evaluate performance improvements by trading off accuracy in our target computations. Our
                                             experimental results reveal that CUDA-aware approximation techniques offer significant performance improvements at the expense of
                                             acceptable accuracy loss.

                                             Keywords
                                             CUDA programming model, GPU computing, approximate computing



                         1. Introduction                                                                                                     grams, but we also propose CUDA-specific methods to tar-
                                                                                                                                             get parallel CUDA threads. Our main contributions are as
                         Heterogeneous computer systems, combining general-                                                                  follows:
                         purpose processors and GPU devices as accelerators, enable
                         high-performance and energy-efficient executions. How-                                                                   • We propose kernel-aware loop perforation by adapt-
                         ever, the applications from various domains such as AI                                                                     ing the loop perforation technique for CUDA pro-
                         acceleration, big-data processing, and high-performance                                                                    grams. To reduce the synchronization overhead, we
                         computing (HPC) with large computing requirements make                                                                     propose partition-level synchronization and block-
                         power consumption an important concern in these systems                                                                    level atomic operations based on CUDA cooperative
                         [1]. Since modern GPU architectures employ complex struc-                                                                  groups and CUDA thread scope atomic functions.
                         tures and the target workloads exploit the massively parallel                                                              Additionally, we eliminate the warp divergence in-
                         resources, energy efficiency becomes critical for large-scale                                                              side CUDA kernel functions to prevent serial execu-
                         GPU executions [2, 3, 4].                                                                                                  tion caused by branch instructions.
                            To solve the conflict between performance and energy                                                                  • We modify the target codes by inserting compiler
                         efficiency, approximate computing maintains high perfor-                                                                   directives enabling our techniques, and generate our
                         mance and low power consumption for applications that can                                                                  approximate versions based on the given compiler
                         tolerate inexact computations. While the architecture-level                                                                options.
                         approximate computing techniques are enabled by modi-                                                                    • We perform an experimental study to evaluate the
                         fying processor units and memory components, software                                                                      impact of the modifications by our approximations.
                         solutions based on compiler transformations or manual code                                                                 Our experimental study includes applications from
                         modifications also offer approximate computations [5, 6].                                                                  different domains to observe the performance and
                         While using inexact hardware or voltage scaling maintains                                                                  accuracy variations for the target execution. Our ex-
                         hardware solutions, techniques like loop perforation or re-                                                                perimental results reveal that CUDA-aware approx-
                         laxed synchronization offer performance-accuracy tradeoffs                                                                 imation techniques offer significant performance
                         at the software level.                                                                                                     improvements at the expense of acceptable accuracy
                            Since GPU systems aim for applications from different                                                                   loss.
                         domains, they employ approximate computing techniques
                         to improve performance and energy efficiency by trading                                                                The remainder of this paper is organized as follows: Sec-
                         with the inaccuracy in the target computations. Besides                                                             tion 2 presents some background on approximate computing
                         inherently error-tolerant graphics and image processing ap-                                                         and the CUDA programming model. We explain our ap-
                         plications [7], general-purpose GPU programs benefit from                                                           proximation methods in Section 3. Then, the experimental
                         approximations with reasonable incorrect computations [8].                                                          results are outlined in Section 4. Section 5 presents relevant
                         While some works reuse generic techniques like perfora-                                                             studies about CUDA approximations. Finally, in Section 6,
                         tion [9], some methods utilize GPU-specific hardware or                                                             we summarize the work with some conclusive remarks.
                         software components to employ approximations [10]. Ad-
                         ditionally, simulation-based evaluations propose hardware
                         modifications by either approximate units or supporting the
                                                                                                                                             2. Background and Motivation
                         approximate computations [11, 12].
                            In this work, we propose software-based approximations
                                                                                                                                             2.1. Approximate Computing
                         for CUDA programs running on GPU architectures. Not                                                                 Approximate computing introduces acceptable inaccuracies
                         only do we adapt the existing techniques for the GPU pro-                                                           into the computing process and promises significant perfor-
                                                                                                                                             mance and energy gains. Some techniques employ the loop
                         RAW’24: The 3rd workshop on Resource AWareness of Systems and Society
                                                                                                                                             perforation approach, which works by skipping some loop
                         (RAW 2024), July 02–05, 2024, Maribor, Slovenia
                         $ isiloz@iyte.edu.tr (I. Öz)                                                                                        iterations to reduce computational overhead [9, 13]. Relaxed
                          0000-0002-8310-1143 (I. Öz)                                                                                       synchronization shortens the waiting time of the threads
                                     © 2024 Copyright for this paper by its authors. Use permitted under Creative Commons License Attribu-
                                     tion 4.0 International (CC BY 4.0).                                                                     that wait for the completion of the other threads’ work

CEUR
                  ceur-ws.org
Workshop      ISSN 1613-0073
Proceedings
__global__ void vectorAdd(double *a, double *b, double *c, int n)
{
    int id = blockIdx.x*blockDim.x+threadIdx.x;

    if (id < n)
        c[id] = a[id] + b[id];
}

int main(int argc, char** argv)
{
    double *d_a, d_b, d_c;
    int n;

    //... memory allocations and copy operations

    int blockSize = 1024;// Number of threads in each thread block
    int gridSize = (int)ceil((float)n/blockSize);

    vecAdd<<>>(d_a, d_b, d_c, n);

    //... memory copy and deallocations
}


Listing 1: Vector addition kernel function and its launch in CUDA.




[14, 15]. Reduced precision computation employs fewer                tures in the CUDA code. Secondly, we replace synchroniza-
compute cycles with insignificant value modifications for            tion primitives with relaxed versions and propose partition-
error-tolerant applications targeting low-precision execu-           level synchronization for threads based on CUDA coopera-
tions [16].                                                          tive groups and block-level atomic operations using CUDA
                                                                     thread scopes. Finally, we remove the warp divergence, a
2.2. GPU Programming Model                                           serial bottleneck in GPU executions.
                                                                        We evaluate target CUDA source codes and perform code
While modern GPU architectures evolve as the requirements            transformations based on compiler directives. While our ap-
of the target applications change, GPU devices employ SIMT           proach requires manual code analysis and modifications to
(Single Instruction Multiple Threads) execution to accelerate        introduce directives in code segments, the semi-automatic
data processing tasks in their parallel execution cores.             configuration enables us to generate target codes that em-
   A program written in the CUDA programming model                   ploy approximations selectively by compiling the code with
[17], which is a parallel programming model for NVIDIA               specific options.
GPU devices, starts its execution in a CPU, allocates memory
space on the GPU, transfers data into GPU global memory,
                                                                     3.1. Kernel-Aware Loop Perforation
and starts a kernel function execution by creating thousands
of threads. Each thread executes the same program (SIMT)             While loop perforation skips some loop iterations in a serial
by processing different parts of the given data. Threads that        program, the same technique can be applied to CUDA pro-
execute on the GPU are part of a compute kernel specified            grams by adapting the perforation accordingly. We consider
by a function. Besides data-parallel applications that can           three approximation versions derived from loop perforation:
benefit from many parallel execution units of GPUs, large-
scale irregular computations utilize the massive degree of           Kernel launch perforation: We skip the iterations of
parallelism and the high memory bandwidth provided by                the loop, which launches one or multiple CUDA kernel
GPUs. Listing 1 presents the CUDA kernel function and ker-           functions at each iteration. The perforation is simply a
nel launch configuration for the vector addition operation.          regular loop perforation with kernel launches per iteration.
For simplicity, we skip the memory allocation and copy op-           In a code segment given in Listing 2 (Fdtd2d program from
erations. The execution launches the vectorAdd function by           Polybench suite [18]), we reduce the number of iterations
specifying the number of blocks and the number of threads            and consequently, the kernel launches by assigning a smaller
in each block. The hardware scheduler schedules the blocks           upper bound (for _PB_TMAX variable in the example code).
into SM (Streaming Multiprocessor) units and thread groups
(warps) into GPU cores inside SMs. Each thread executes              Kernel launch configuration perforation: In data-
the kernel function and performs the addition operation              parallel CUDA programs, CUDA threads execute implicit
based on its global thread identifier.                               loops in parallel by performing the computations that be-
                                                                     long to one or more iterations of the serial program loop.
3. Approximation for CUDA                                            We launch target kernel executions by reducing the number
                                                                     of threads in the configuration; hence, the original loop in
   Programs                                                          the serial program is perforated. In a code segment given
                                                                     in Listing 3 (Convolution2D program from Polybench suite
We propose three main approximations for target CUDA                 [18]), we modify the block or grid configuration parameters
programs. Firstly, we exploit loop perforation by adapt-             of the convolution2D_kernel kernel by reducing the X or Y
ing the popular method for loop and loop-similar struc-              dimensions of the grid.
...
for(int t = 0; t < _PB_TMAX; t++)
{
    fdtd_step1_kernel<<>>(nx, ny, _fict_gpu, ex_gpu, ey_gpu, hz_gpu, t);
    cudaDeviceSynchronize();
    fdtd_step2_kernel<<>>(nx, ny, ex_gpu, ey_gpu, hz_gpu, t);
    cudaDeviceSynchronize();
    fdtd_step3_kernel<<>>(nx, ny, ex_gpu, ey_gpu, hz_gpu, t);
    cudaDeviceSynchronize();
}
...


Listing 2: The Fdtd2d code with kernel function calls inside a loop.



...
dim3 block(DIM_THREAD_BLOCK_X, DIM_THREAD_BLOCK_Y);
dim3 grid(ceil(((float)NI) / ((float)block.x)), ceil(((float)NJ) / ((float)block.y)));

convolution2D_kernel <<< grid,block >>> (ni, nj, A_gpu,B_gpu);
...


Listing 3: The Convolution2D code kernel launch configuration.




...                                                                    3.2. Relaxed Synchronization
__global__ void mean_kernel(int m, int n, DATA_TYPE *
     mean, DATA_TYPE *data)                                            Multiple CUDA threads require time-consuming synchro-
{                                                                      nization to access shared data or resolve data dependencies,
 int j = blockIdx.x * blockDim.x + threadIdx.x;                        utilizing atomic operations (like atomicAdd) and barrier op-
                                                                       erations (like __syncthreads()), respectively. The relaxed
 if (j < _PB_M)
 {
                                                                       synchronization offers performance gains by synchronizing
    mean[j] = 0.0;                                                     fewer threads in exchange for output accuracy loss. We
                                                                       consider two main relaxations based on CUDA cooperative
      int i;                                                           groups and CUDA thread scopes:
      for(i = 0; i < _PB_N; i++)
      {
          mean[j] += data[i * M + j];                                  Partition-level synchronization: CUDA threads within
      }                                                                a block can cooperate by synchronizing their execution
      mean[j] /= (DATA_TYPE)FLOAT_N;                                   to coordinate memory accesses. The programmer can de-
 }
                                                                       fine synchronization points by calling the __syncthreads()
}
...
                                                                       function, which acts as a barrier and makes waiting for
                                                                       all threads. While CUDA employs block-level synchroniza-
Listing 4: The Covariance code with loop structures inside a           tion by __syncthreads() function, __syncwarp() function,
kernel function mean_kernel.                                           which synchronizes the threads within a warp, has become
                                                                       available on CUDA 9. This is important for porting code
                                                                       to modern GPU architectures after Volta, in which threads
                                                                       within a warp can be scheduled separately. Additionally, the
Intra-kernel loop perforation: We perform the stan-                    Cooperative Groups API [19] provides a rich set of thread-
dard loop perforation method for the code inside kernel                synchronization primitives by forming partitions with a set
functions. In a code segment given in Listing 4 (Covariance            of threads. Listing 5 presents different code snippets to or-
program from Polybench suite [18]), we reduce the number               ganize groups of threads. While the first group, blockgroup,
of loop iterations inside mean_kernel kernel (_PB_N variable           represents all the threads in a thread block, warpgroup repre-
in the example code).                                                  sents all the threads in a warp. If we want to synchronize the
                                                                       threads in those groups, the behavior will be the same with
  We modify each program code by inserting                             __syncthreads() and __syncwarp() functions, respectively.
compiler directives for a set of loop perforation                         For implementing the approximation, first, we search
types. Specifically, we define four directives: KER-                   for all __syncthreads() function calls in the target kernel
NEL_LAUNCH_PERFORATION,               GRID_PERFORATION,                functions and configure the synchronization level for each
BLOCK_PERFORATION, LOOP_PERFORATION, and compile                       synchronization point. Specifically, we either completely
the programs by enabling the directives with specific values,          skip __syncthreads() (SKIP) or replace it with a relaxed ver-
which represent the perforation rate as the reduction ratio            sion. For relaxing synchronization, we choose __syncwarp()
of the target loop. By enabling the chosen perforation                 (WARP) or utilize cooperative thread groups (the details
type(s) and rate(s) at compile time, we evaluate the impacts           are given below). We modify each program code and inject
on the execution.                                                      #ifdef directives to guide the compiler based on user prefer-
                                                                       ences. For each __syncthreads() code block, we define one
                                                                       directive and compile the code by specifying one or more
                                                                       directives.
// Cooperative group for the current thread block
auto blockgroup = cooperative_groups::this_thread_block();

// Cooperative group for each warp in the thread block
auto warpgroup = cooperative_groups::tiled_partition<32>(threadblock);

// Cooperative group for each 16 threads in the thread block
auto subwarp16 = cooperative_groups::tiled_partition<16>(threadblock);

// Cooperative group for all currently coalesced threads in the warp
auto coalescedgroup = cooperative_groups::coalesced_threads();

// Thread block groups can sync
blockgroup.sync();


Listing 5: CUDA cooperative groups [19].



//Replaced code version 1 (4TILE)
thread_group tile32 = tiled_partition(this_thread_block(), 32);
thread_group tile4 = tiled_partition(tile32, 4);
tile4.sync();

//Replaced code version 2 (ACTIVE)
thread_group active = coalesced_threads();
active.sync();


Listing 6: Partition-level synchronization configurations.




   For our partition-level approach, we define two thread        the same time. Due to SIMD execution model, when threads
partitions (as given in Listing 6): 1) 4TILE: Cooperative        in the same warp need to perform different operations, the
thread groups with four threads in the corresponding warp,       execution of the different branches is serialized, thus hurt-
2) ACTIVE: Currently coalesced threads in the warp. When         ing performance improvement that could be gained from
data-dependent conditional branches in the code cause            parallelism. Figure 1 presents an example scenario for warp
threads within a warp to diverge, the SM disables (deac-         divergence. The eight threads (assuming we have an 8-
tivates) threads that do not take the branch. The threads        thread warp size for simplicity) start the kernel execution,
that remain active on the path are referred to as coalesced.     then at Branch point, there is an if statement that causes
                                                                 different path executions. While four threads execute the
Block-level atomic operations: While the atomic opera-           instruction at Path A, the other four continue the execution
tions in standard C or C++ are uniform, the CUDA program-        at Path B. When the first four threads execute Path A, the
ming model offers atomic functions at different scopes. A        others must wait and perform no operation. The marked
thread scope specifies the set of threads that can synchronize   execution prevents full warp utilization by activating only
with each other using atomic operations. Atomic functions        four threads simultaneously in an 8-thread warp structure.
with _system suffix (e.g., atomicAdd_system) are atomic at
system scope, where the system refers to the system running
on multiple GPUs and CPUs. Atomic functions without a
suffix (e.g., atomicAdd) are atomic at device scope, where
the device refers to the target GPU device. Atomic func-
tions with _block suffix (e.g., atomicAdd_block) are atomic
at thread block scope, which refers to the synchronization
of the threads executing on the same thread block.
   In block-level atomic operations, we target that the
threads perform atomic operations at the largest thread
block scope. Like our synchronization approach, we search
for all atomic functions in the target CUDA code and re-
duce the atomic scope accordingly. For instance, we re-
place atomicAdd function calls with atomicAdd_block, or
we completely remove the function call. Hence, we aim
for atomic operations with fewer threads than the original
code. Similarly, we could replace atomicAdd_system or skip
atomicAdd_block functions.                                       Figure 1: SIMD Warp Divergence [20].


3.3. Warp Divergence Elimination                                    To eliminate the divergence overhead, as an approxima-
                                                                 tion method, we execute only one path in case of multiple
CUDA threads are executed in groups of 32 threads (warps),
                                                                 paths in a warp. For instance, for the code given in Listing
and all threads in a warp execute the same instruction at
                                                                 7 (from Grappolo application [21]), we configure to execute
Path 1, Path 2, or Path 3. Alternatively, we completely skip   grid-level kernel launch configuration perforation, and
the divergent code segment in our evaluations.                 block-level kernel launch configuration perforation, we per-
                                                               form 90% and 80% perforation rates. If the program does
...                                                            not support the target approximation (e.g., Correlation does
if (currCId == FLAG_FREE) {                                    not have a kernel launch inside a loop), we simply do not
    /*Path 1*/                                                 have the corresponding result in our evaluation. The values
}
                                                               in Figure 2 present 1/Speedup and the rate of incorrectly
if (currCId == (1 + dataItem->cId)) {
    /*Path 2*/
                                                               computed elements. We define the performance in terms
}                                                              of speedup, the ratio of the compute time for the original
else                                                           execution to the time for the approximate execution, and
{                                                              report the 1/Speedup values in our results. For instance,
    /*Path 3*/
                                                               the execution time for the original Correlation execution is
}
...
                                                               1.785 milliseconds, and it computes 4194304 array elements.
                                                               When we perforate the kernel function loops by 90% (Loop
Listing 7: SIMD warp divergence code example.                  (90%)), we have 1.302 milliseconds and 793356 incorrect
                                                               computations. Therefore, the performance improvement
                                                               rate equals 1.302/1.785=0.73, and the rate of the incorrect
                                                               computations is 793356/4194304=0.19, shown in Figure 2.
                                                               By reporting performance improvement and inaccuracy val-
4. Experimental Study                                          ues in this way, one can evaluate performance gains and
                                                               incorrect results for each approximation and make design
4.1. Experimental Setup                                        decisions. Based on the program characteristics, each ap-
                                                               proximation affects the execution outcome differently. We
To evaluate our approximation methods, we select CUDA
                                                               can have up to 60% performance improvements (Loop (80%)
applications from Polybench [18] and Gardenia [22] bench-
                                                               for Correlation) in exchange for 40% of the elements incor-
mark suites and utilize an optimized CUDA implementation
                                                               rectly computed. Some approximations offer good tradeoff
of the Louvain community detection algorithm, namely
                                                               points, like grid-level kernel launch configuration perfora-
Grappolo [21]. While Polybench applications mostly employ
                                                               tions (Grid (90%) and Grid (80%)) in Fdtd2d. We can have
data-parallel computations with multiple loop structures,
                                                               20% and 30% performance improvements by losing 30% and
Gardenia implements a set of graph algorithms that include
                                                               50% of correct computations. On the other hand, there is
synchronization primitives. Grappolo, with computationally
                                                               no performance improvement with small inaccuracy values
intensive and complex structures, includes code segments
                                                               (like kernel launch configuration perforations in Covariance)
for our evaluations on relaxed synchronization and warp
                                                               or intolerable output loss with improvement in execution
divergence-based approximations.
                                                               times (like loop perforations in Syrk).
   We compile our programs with CUDA 12.1 [23] and run
our approximation experiments in a system with an NVIDIA
GeForce RTX 3050 Ti Mobile GPU device. The GPU device,         4.2.2. Relaxed Synchronization
built on Ampere architecture [24], has 4 GB GDDR6 mem-         We evaluate the Betweenness Centrality (bc) in the Gardenia
ory.                                                           benchmark suite [22], which has four different implementa-
                                                               tions. For a sample graph (soc-LiveJournal1 [25]), we execute
4.2. Experimental Results                                      each version and select the one with the lowest execution
                                                               time. Since the version already employs optimizations, we
We evaluate our three main approximations for target CUDA      apply our approximation methods to that version for fair
programs separately. We execute both original and approxi-     comparison. The implementation (i.e., bc_topo_lb) has four
mated versions, measure GPU execution times, and collect       main kernel functions with synchronization primitives (i.e.,
result outputs. By comparing execution time and output         __syncthreads()). We apply our relaxed synchronization
accuracies, we perform a tradeoff analysis for target com-     techniques for each seven __syncthreads() function call in
putations.                                                     the target kernel functions and perform four specific modi-
                                                               fications: 1) SKIP: Remove __syncthreads(), 2) WARP: Syn-
4.2.1. Kernel-Aware Loop Perforation                           chronize threads in the same warp, 3) 4TILE: Synchronize
For our loop perforation techniques, we select six programs    four threads in the same cooperative group, 4) ACTIVE:
from the Polybench benchmark suite [18]. The programs          Synchronize coalesced threads. Finally, we have 28 differ-
have data-parallel characteristics and each employs differ-    ent versions. We execute those versions with 19 different
ent loop structures. We execute Correlation, Covariance,       datasets. We observe execution time and output differences
Syrk, Fdtd2d with STANDARD input sizes and Jacobi-2D,          for only a subset of our executions, specifically, relaxations
and 2DConv with LARGE input sizes to have longer execu-        for three __syncthreads() function calls on only one kernel
tion times. We collect GPU execution times and incorrect       function. For three synchronization points, we also consider
computations by comparing them with the original output.       the relaxation of their combinations.
Since the programs work with array structures and compute         Table 1 presents the execution times and the number of
array elements as the final output, we evaluate the number     incorrect computations in the observed output for the spec-
of array elements that are computed incorrectly.               ified graphs. We can observe that SKIP, WARP, and ACTIVE
   Figure 2 demonstrates performance improvement and in-       mostly outperform 4TILE, probably due to the overhead of
accuracy values for the programs when our loop-perforation     fine-grained group creation. While relaxing individual syn-
methods are applied. For each applicable method, namely,       chronization points (i.e., SYNC 1, SYNC 2, SYNC 3) offers
kernel launch perforation, intra-kernel loop perforation,      performance gains significantly with non-significant accu-
         Figure 2: Speedup-Inaccuracy variation for loop perforation approximation methods.


    Table 1
    Execution time and incorrect computations (out of given expected correct values) for relaxed synchronization methods for bc
    application.
                                   ljournal-2008                   socLiveJournal                    cage15
                                       Time           Incorrect         Time            Incorrect     Time       Incorrect
                                                     (5,363,260)                       (4,847,571)              (5,154,859)
         ORIGINAL                      61.156             0             57.116              0         47.096         0
                        SKIP           60.637            954            57.116            276        46.780       28,326
          SYNC 1        WARP           60.199            793            53.611            355        47.109       70,611
                        4TILE          60.579            754            57.205            227        46.939       76,002
                       ACTIVE          60.205           1113            55.431            292        47.164       71,563
                        SKIP           59.770          19,888           52.756            1636       47.425       486,970
          SYNC 2        WARP           59.314          11,230           52.485            1693       47.811       498,751
                        4TILE          61.364          14,087           52.300            1522       48.465       303,306
                       ACTIVE          59.397           8345            52.532            1647       47.850       463,191
                        SKIP           61.328            64             53.996             36        47.198       15,531
          SYNC 3        WARP           60.643           1996            53.774             38        47.843       14,341
                        4TILE          61.374            137            53.961             34        47.186       13,383
                       ACTIVE          60.744            160            53.872             32        47.775       13,975
                        SKIP           56.800          20,137           50.572            1739       46.420       98,691
         SYNC 1+2       WARP           56.377          17,035           50.401            1633       46.879       104,232
                        4TILE          57.779          12,311           51.199            1678       47.217       138,951
                       ACTIVE          56.195          22,491           50.495            1657       46.908       84,210
                        SKIP           57.088          20,346           50.871            1729       46.966       85,029
        SYNC 1+2+3      WARP           56.747          20,719           50.774            1600       47.320       62,026
                        4TILE          57.801          14,807           51.513            1649       47.678       59,262
                       ACTIVE          56.608          17,248           50.880            1556       47.352       87,300


racy loss, the combinations (i.e., SYNC 1+2, SYNC 1+2+3)            vain community detection algorithm [26]. Louvain is a
further improve the performance without hurting the out-            greedy graph processing method that assigns each vertex to
put quality much. While the accuracy loss depends on the            a community, which maximizes the overall Modularity and
target dataset, we see the most promising relaxation options        generates a new graph in which the communities become
for performance gains (around 8%-10%) with the SYNC 1+2             new vertices. Since the output metric, Modularity, does not
version.                                                            present an exact result, trading the output accuracy with
   Since bc has no atomic operations, we consider another           performance improvement can be an interesting evaluation
application to observe the impact of our approximation              for the execution. The Modularity metric evaluation de-
techniques for atomic operations. We utilize Grappolo code          pends on the application domain utilizing the community
[21], a highly-optimized CUDA implementation of the Lou-            detection, however, a Modularity value close to 1 presents
         Figure 3: Execution time-Modularity values for relaxing atomic operations in Grappolo.



higher quality output. While the Grappolo employs both syn-        tion. Firstly, we execute only one path out of three, but
chronization and atomic operation primitives, we evaluate          our execution does not end (infinite loop) with each path
only atomic operations and perform our relaxation methods.         choice. Then, we apply a different strategy by eliminating
Specifically, for atomicAdd and atomicCAS function calls,          the code in the target path executions and returning the
we either replace them with the non-atomic operation or            previously computed value with no computation. For this
the block-level atomic function calls (i.e., atomicAdd_block       method, our execution is completed in a shorter time with
or atomicCAS_block).                                               lower Modularity values.
   Figure 3 presents execution time and modularity values             Table 2 presents execution time and modularity values for
as the performance and the accuracy metrics, respectively.         the original execution and our approximate version. While
Besides individual atomic operations, we relax the combi-          we can see a decrease in all execution times, the approx-
nations of the atomic operations to see the impact on the          imation also destroys modularity values with one excep-
outcome. In our target program, we have five atomicAdd             tion, namely the wb-edu dataset. Since this approximation
and three atomicCAS function calls. We label the relaxations       completely eliminates some code segments, accuracy loss
by considering the index and type of the method. Specif-           becomes inevitable for most cases, and it requires a more
ically, we use SKIP or BLOCK as the prefix and the order           rigorous analysis of the target code.
of the corresponding function as the suffix. For instance,
SKIP_ATOMICADD_1 replaces the first atomicAdd with the
non-atomic operation; BLOCK_ATOMICADD_3 replaces the                5. Related Work
third atomicAdd with atomicAdd_block. For the combined
                                                                   SAGE [7] presents a static compiler that generates a set of
relaxations, we concatenate the index of each operation
                                                                   approximated CUDA kernels and a runtime system that em-
such as SKIP_ATOMICADD_1_2. We only select a subset of
                                                                   ploys selective discarding of atomic operations, data pack-
the combinations since it is not practical to execute all of
                                                                   ing, and thread fusion optimizations. It yields 2.5× speedup
them. While we conduct experiments for 19 datasets, we
                                                                   with less than 10% quality loss for machine learning and
include five of them that present the most interesting design
                                                                   image processing kernels. While SAGE proposes approxi-
points. All five graphs demonstrate significant performance
                                                                   mations for CUDA computations and significantly improves
improvements with little modularity losses. Depending on
                                                                   performance, it relies on generic approximation methods
the modularity evaluation of the target domain utilizing the
                                                                   instead of CUDA-specific techniques.
community detection, one can easily prefer approximated
                                                                      Freytag et al. [27] propose efficient executions for sci-
versions. The executions that have large modularity val-
                                                                   entific simulation applications by building multiple kernel
ues in the original version exhibit significant performance
                                                                   implementations with different precision levels. They exe-
improvements without hurting the modularity very much.
                                                                   cute approximated kernel versions by switching from one
Especially, SKIP_ATOMIC_ADD_1_2 version promises up to
                                                                   version to another at runtime based on Target Output Qual-
3x performance gains with 0.01 modularity loss.
                                                                   ity (TOQ) scenarios. By employing execution configurations
                                                                   based on an analysis of the accuracy loss, the experiments
4.2.3. Warp Divergence Elimination                                 reveal high-performance and energy-efficient executions for
We utilize Grappolo for our warp divergence elimination            target precision levels. While the authors build application-
method due to its complex structure that employs branch            layer approximations for the target code, they modify the
instructions inside kernel functions. We work with two             precision levels of the target code while not introducing
kernel functions and perform different divergence elimina-         CUDA-specific methods.
    Table 2
    Execution time and Modularity values with warp divergence elimination for Grappolo.
                                                Original                   Approx.
                                Dataset          Time         Modularity    Time          Modularity
                                  relat9          1.206         0.491       0.616           0.254
                                 cage15           1.341         0.893       1.077           0.727
                                   rel9           1.094         0.458       0.446           0.253
                                ljournal          2.034         0.759       1.573           0.588
                                  rgg23           1.295         0.991       1.206           0.718
                            soc-LiveJournal1      2.042         0.753       1.606           0.603
                                wb-edu            3.468         0.995       1.621           0.980


   Liu et al. [28] present cuSpAMM, the CUDA adaptation             [3] Q. Zeng, Y. Du, K. Huang, K. K. Leung, Energy-efficient
of the Sparse Approximate Matrix Multiply algorithm, by                 resource management for federated edge learning with
utilizing thread parallelism, memory tiling, and the tensor             CPU-GPU heterogeneous computing, IEEE Transac-
cores in multiple GPU devices. While the proposed work                  tions on Wireless Communications 20 (2021) 7947–
implements an approximation algorithm by considering                    7962.
GPU-specific features, the implementation, rather than ap-          [4] V. Raca, S. W. Umboh, E. Mehofer, B. Scholz, Runtime
proximation, relies on GPU optimization techniques.                     and energy constrained work scheduling for hetero-
                                                                        geneous systems, The Journal of Supercomputing 78
                                                                        (2022) 17150–17177.
6. Conclusions and Future Work                                      [5] S. Mittal, A survey of techniques for approximate
                                                                        computing, ACM Computing Surveys 48 (2016).
In this work, we propose CUDA-specific approximation
                                                                    [6] P. Stanley-Marbell, A. Alaghi, M. Carbin, E. Darulova,
methods based on loop perforation, relaxed synchroniza-
                                                                        L. Dolecek, A. Gerstlauer, G. Gillani, D. Jevdjic,
tion, and warp divergence elimination. We define approxi-
                                                                        T. Moreau, M. Cacciotti, A. Daglis, N. E. Jerger, B. Fal-
mations as compiler directives and enable them for target
                                                                        safi, S. Misailovic, A. Sampson, D. Zufferey, Exploit-
executions. Our experimental results demonstrate that our
                                                                        ing errors for efficiency: A survey from circuits to
approximation techniques promise good performance im-
                                                                        applications, ACM Computing Surveys 53 (2020).
provements without hurting output accuracy significantly.
                                                                        doi:10.1145/3394898.
   Our approximations are enabled based on compiler direc-
                                                                    [7] M. Samadi, J. Lee, D. A. Jamshidi, A. Hormati,
tives. While the directives offer some level of automation,
                                                                        S. Mahlke, Sage: Self-tuning approximation for graph-
we can extend our work by building a fully automated tool
                                                                        ics engines, in: 2013 46th Annual IEEE/ACM Inter-
that performs source-to-source compiler transformations.
                                                                        national Symposium on Microarchitecture (MICRO),
Thus, we can easily generate our approximated versions.
                                                                        2013.
Moreover, a design space exploration technique potentially
                                                                    [8] D. Peroni, M. Imani, H. Nejatollahi, N. Dutt, T. Rosing,
helps to choose the best design points considering perfor-
                                                                        Arga: Approximate reuse for GPGPU acceleration, in:
mance improvements and inaccuracy values.
                                                                        2019 56th ACM/IEEE Design Automation Conference
   While approximate computing offers performance im-
                                                                        (DAC), 2019, pp. 1–6.
provements, it is essential to evaluate the power consump-
                                                                    [9] D. Maier, B. Cosenza, B. Juurlink, Local memory-
tion of the target execution. We can extend our work by
                                                                        aware kernel perforation, in: Proceedings of the
including energy measurements for GPU devices and in-
                                                                        2018 International Symposium on Code Generation
clude that criterion as our resource-aware evaluation.
                                                                        and Optimization, CGO 2018, Association for Comput-
                                                                        ing Machinery, New York, NY, USA, 2018, p. 278–287.
Acknowledgments                                                         doi:10.1145/3168814.
                                                                   [10] A. Li, S. L. Song, M. Wijtvliet, A. Kumar, H. Corporaal,
This work was supported by the Scientific and Technological             Sfu-driven transparent approximation acceleration on
Research Council of Turkey (TÜBİTAK), Grant No: 122E395.                GPUs, in: Proceedings of the 2016 International Con-
This work is partially supported by CERCIRAS COST Action                ference on Supercomputing, ICS ’16, Association for
CA19135 funded by COST Association.                                     Computing Machinery, New York, NY, USA, 2016.
                                                                   [11] R. Garcia, F. Asgarinejad, B. Khaleghi, T. Rosing,
                                                                        M. Imani, Trulook: A framework for configurable
References                                                              GPU approximation, in: 2021 Design, Automation
                                                                        and Test in Europe Conference and Exhibition (DATE),
 [1] S. Mittal, J. S. Vetter, A survey of methods for ana-
                                                                        2021, pp. 487–490. doi:10.23919/DATE51398.2021.
     lyzing and improving GPU energy efficiency, ACM
                                                                        9474239.
     Computing Surveys 47 (2014). URL: https://doi.org/10.
                                                                   [12] F. Vaverka, V. Mrazek, Z. Vasicek, L. Sekanina, Tfap-
     1145/2636342. doi:10.1145/2636342.
                                                                        prox: Towards a fast emulation of dnn approximate
 [2] K. Ma, X. Li, W. Chen, C. Zhang, X. Wang, GreenGPU:
                                                                        hardware accelerators on GPU, in: 2020 Design,
     A holistic approach to energy efficiency in GPU-CPU
                                                                        Automation and Test in Europe Conference and Ex-
     heterogeneous architectures, in: 2012 41st Interna-
                                                                        hibition (DATE), 2020, pp. 294–297. doi:10.23919/
     tional Conference on Parallel Processing, 2012, pp.
                                                                        DATE48585.2020.9116299.
     48–57.
                                                                   [13] D. Maier, B. Juurlink, Model-based loop perforation,
     in: Euro-Par 2021: Parallel Processing Workshops,
     Springer International Publishing, Cham, 2022, pp.
     549–554.
[14] K. Iliakis, H. Timko, S. Xydis, P. Tsapatsaris, D. Soudris,
     Enabling large scale simulations for particle acceler-
     ators, IEEE Transactions on Parallel and Distributed
     Systems 33 (2022) 4425–4439. doi:10.1109/TPDS.
     2022.3192707.
[15] A. L. C. Bueno, N. de La Rocque Rodriguez, E. D.
     Sotelino, Adaptive relaxed synchronization through
     the use of supervised learning methods, Future Gen-
     eration Computer Systems 106 (2020) 260–269.
[16] S. Cherubin, G. Agosta, Tools for reduced precision
     computation: A survey, ACM Computing Surveys 53
     (2020). URL: https://doi.org/10.1145/3381039. doi:10.
     1145/3381039.
[17] D. B. Kirk, W. mei W. Hwu, Programming Massively
     Parallel Processors (Third Edition), Morgan Kaufmann,
     2017.
[18] S. Grauer-Gray, L. Xu, R. Searles, S. Ayalasomayajula,
     J. Cavazos, Auto-tuning a high-level language targeted
     to GPU codes, 2012 Innovative Parallel Computing
     (InPar), 2012.
[19] M. Harris, K. Perelygin, Cooperative groups: Flexible
     CUDA thread programming, https://developer.nvidia.
     com/blog/cooperative-groups/, 2017.
[20] T. M. Aamodt, W. W. L. Fung, T. G. Rogers,
     M. Martonosi, General-Purpose Graphics Processor
     Architecture, 2018.
[21] A. K. Mahantesh Halappanavar, Howard (Hao) Lu,
     S. Ghosh, Grappolo community detection, https://
     github.com/ECP-ExaGraph/grappolo, 2024.
[22] Z. Xu, X. Chen, J. Shen, Y. Zhang, C. Chen, C. Yang,
     Gardenia: A graph processing benchmark suite for
     next-generation accelerators, ACM Journal on Emerg-
     ing Technologies in Computing Systems 15 (2019).
     URL: https://doi.org/10.1145/3283450. doi:10.1145/
     3283450.
[23] Nvidia, CUDA toolkit 12.1, https://developer.nvidia.
     com/cuda-12-1-0-download-archive, 2024.
[24] Nvidia, Nvidia ampere ga102 GPU architecture
     white paper, https://www.nvidia.com/content/PDF/
     nvidia-ampere-ga-102-gpu-architecture-whitepaper-v2.
     1.pdf, 2021.
[25] J. Leskovec, A. Krevl, SNAP Datasets: Stanford large
     network dataset collection, http://snap.stanford.edu/
     data, 2014.
[26] V. Blondel, J. Guillaume, R. Lambiotte, E. Mech, Fast
     unfolding of communities in large networks, Journal of
     Statistical Mechanics: Theory and Experiment (2008).
[27] G. Freytag, C. A. Künas, P. Rech, P. O. A. Navaux, In-
     terleaved execution of approximated cuda kernels in
     iterative applications, in: 2024 32nd Euromicro In-
     ternational Conference on Parallel, Distributed and
     Network-Based Processing (PDP), 2024, pp. 60–67.
     doi:10.1109/PDP62718.2024.00017.
[28] X. Liu, Y. Liu, H. Yang, M. Dun, B. Yin, Z. Luan,
     D. Qian, Accelerating approximate matrix multipli-
     cation for near-sparse matrices on gpus, Journal of
     Supercomputing 78 (2022) 11464–11491. URL: https:
     //doi.org/10.1007/s11227-022-04334-5. doi:10.1007/
     s11227-022-04334-5.