packages feed

cuda-0.1: examples/src/scan/scan.cu

/* -----------------------------------------------------------------------------
 *
 * Module    : Scan
 * Copyright : (c) 2009 Trevor L. McDonell
 * License   : BSD
 *
 * ---------------------------------------------------------------------------*/

#include "scan.h"

#include "utils.h"
#include "operator.h"
#include "cudpp/cudpp_globals.h"
#include "cudpp/scan_kernel.cu"
#include "cudpp/vector_kernel.cu"

template <typename T>
struct scan_plan
{
    T           **block_sums;
    size_t      num_levels;
};

static inline unsigned int
calc_num_blocks(unsigned int N)
{
    return max(1u, (unsigned int)ceil((double)N / (SCAN_ELTS_PER_THREAD * CTA_SIZE)));
}


/*
 * This is the CPU-side workhorse of the scan operation, invoking the kernel on
 * each of the reduction blocks.
 */
template <class op, typename T, bool backward, bool exclusive>
static void
scan_recursive
(
    const T             *in,
    T                   *out,
    scan_plan<T>        *plan,
    int                 N,
    int                 level
)
{
    size_t      num_blocks = calc_num_blocks(N);
    bool        is_full    = N == num_blocks * SCAN_ELTS_PER_THREAD * CTA_SIZE;

    dim3        grid(num_blocks, 1, 1);
    dim3        block(CTA_SIZE, 1, 1);
    size_t      smem = sizeof(T) * CTA_SIZE * 2;

#define MULTIBLOCK      0x01
#define FULLBLOCK       0x04
    int traits = 0;
    if (num_blocks > 1) traits |= MULTIBLOCK;
    if (is_full)        traits |= FULLBLOCK;

    /*
     * Set up execution parameters, and execute the scan
     */
    switch (traits)
    {
    case 0:
        scan4
            < T, ScanTraits<T, op, backward, exclusive, false, false, false> >
            <<<grid, block, smem>>>(out, in, NULL, N, 1, 1);
        break;

    case MULTIBLOCK:
        scan4
            < T, ScanTraits<T, op, backward, exclusive, false, true, false> >
            <<<grid, block, smem>>>(out, in, plan->block_sums[level], N, 1, 1);
        break;

    case FULLBLOCK:
        scan4
            < T, ScanTraits<T, op, backward, exclusive, false, false, true> >
            <<<grid, block, smem>>>(out, in, NULL, N, 1, 1);
        break;

    case MULTIBLOCK | FULLBLOCK:
        scan4
            < T, ScanTraits<T, op, backward, exclusive, false, true, true> >
            <<<grid, block, smem>>>(out, in, plan->block_sums[level], N, 1, 1);
        break;

    default:
        assert(!"Non-exhaustive patterns in match");
    }

    /*
     * After scanning the sub-blocks, we now need to combine those results by
     * taking the last value from each sub-block, and adding that to each of the
     * successive blocks (i.e. scan across the sub-computations)
     */
    if (num_blocks > 1)
    {
        T *sums = plan->block_sums[level];

        scan_recursive
            <op, T, backward, true>
            (sums, sums, plan, num_blocks, level+1);

        vectorAddUniform4
            <T, op, SCAN_ELTS_PER_THREAD>
            <<<grid,block>>>
            (out, sums, N, 4, 4, 0, 0);
    }

#undef MULTIBLOCK
#undef FULLBLOCK
}


/*
 * Allocate temporary memory used by the scan.
 */
template <typename T>
static void
scan_init(int N, scan_plan<T> *plan)
{
    size_t level        = 0;
    size_t elements     = N;
    size_t num_blocks;

    /*
     * Determine how many intermediate block-level summations will be required
     */
    for (elements = N; elements > 1; elements = num_blocks)
    {
        num_blocks = calc_num_blocks(elements);

        if (num_blocks > 1)
            ++level;
    }

    plan->block_sums = (T**) malloc(level * sizeof(T*));
    plan->num_levels = level;

    /*
     * Now, allocate the necessary storage at each level
     */
    for (elements = N, level = 0; elements > 1; elements = num_blocks, level++)
    {
        num_blocks = calc_num_blocks(elements);

        if (num_blocks > 1)
            cudaMalloc((void**) &plan->block_sums[level], num_blocks * sizeof(T));
    }
}


/*
 * Clean up temporary memory used by the scan
 */
template <typename T>
static void
scan_finalise(scan_plan<T> *p)
{
    for (size_t l = 0; l < p->num_levels; ++l)
        cudaFree(p->block_sums[l]);

    free(p->block_sums);
}


/*
 * Apply a binary operator to an array similar to `fold', but return a
 * successive list of values reduced from the left. The reduction will take
 * place in parallel, so the operator must be associative.
 */
template <class op, typename T, bool backward, bool exclusive>
void
scan
(
    const T     *in,
    T           *out,
    int         length
)
{
    scan_plan<T> plan;
    scan_init<T>(length, &plan);

    scan_recursive<op, T, backward, exclusive>(in, out, &plan, length, 0);

    scan_finalise<T>(&plan);
}


// -----------------------------------------------------------------------------
// Instances
// -----------------------------------------------------------------------------

void scanl_plusf(float *in, float *out, int N)
{
    scan< Plus<float>, float, false, true >(in, out, N);
}

void scanl1_plusf(float *in, float *out, int N)
{
    scan< Plus<float>, float, false, false >(in, out, N);
}