packages feed

accelerate-0.9.0.0: cubits/fold.inl

/* -----------------------------------------------------------------------------
 *
 * Kernel      : Fold
 * Copyright   : [2008..2011] Manuel M T Chakravarty, Gabriele Keller, Sean Lee, Trevor L. McDonell
 * License     : BSD3
 *
 * Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
 * Stability   : experimental
 *
 * Reduce the innermost dimension of a multidimensional array to a single value
 * with a binary associative function
 *
 * ---------------------------------------------------------------------------*/

#include "reduce.inl"


extern "C"
__global__ void
fold
(
    ArrOut              d_out,
    const ArrIn0        d_in0,
    const DimOut        shOut,
    const DimIn0        shIn0
)
{
    extern volatile __shared__ void* s_ptr[];
    ArrOut s_data = partition(s_ptr, blockDim.x);

    const Ix num_elements = indexHead(shIn0);
    const Ix num_segments = size(shOut);

    const Ix num_vectors  = blockDim.x / warpSize * gridDim.x;
    const Ix thread_id    = blockDim.x * blockIdx.x + threadIdx.x;
    const Ix vector_id    = thread_id / warpSize;
    const Ix thread_lane  = threadIdx.x & (warpSize - 1);

    /*
     * Each warp reduces elements along a projection through an innermost
     * dimension to a single value
     */
    for (Ix seg = vector_id; seg < num_segments; seg += num_vectors)
    {
        const Ix    start = seg   * num_elements;
        const Ix    end   = start + num_elements;
              TyOut sum;

        if (num_elements > warpSize)
        {
            /*
             * Ensure aligned access to global memory, and that each thread
             * initialises its local sum.
             */
            Ix i = start - (start & (warpSize - 1)) + thread_lane;
            if (i >= start)
                sum = get0(d_in0, i);

            if (i + warpSize < end)
            {
                TyOut tmp = get0(d_in0, i + warpSize);

                if (i >= start) sum = apply(sum, tmp);
                else            sum = tmp;
            }

            /*
             * Now, iterate along the inner-most dimension collecting a local sum
             */
            for (i += 2 * warpSize; i < end; i += warpSize)
                sum = apply(sum, get0(d_in0, i));
        }
        else if (start + thread_lane < end)
        {
            sum = get0(d_in0, start + thread_lane);
        }

        /*
         * Each thread puts its local sum into shared memory, then cooperatively
         * reduce the shared array to a single value.
         */
        set(s_data, threadIdx.x, sum);
        sum = reduce_warp_n(s_data, sum, min(num_elements, warpSize));

        /*
         * Finally, the first thread writes the result for this segment
         */
        if (thread_lane == 0)
        {
#ifndef INCLUSIVE
            sum = num_elements > 0 ? apply(sum, identity()) : identity();
#endif
            set(d_out, seg, sum);
        }
    }
}