All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spgemm_rmerge.hpp
Go to the documentation of this file.
#ifndef VIENNACL_LINALG_CUDA_SPGEMM_RMERGE_HPP_
#define VIENNACL_LINALG_CUDA_SPGEMM_RMERGE_HPP_
/* =========================================================================
Copyright (c) 2010-2016, Institute for Microelectronics,
Institute for Analysis and Scientific Computing,
TU Wien.
Portions of this software are copyright by UChicago Argonne, LLC.
-----------------
ViennaCL - The Vienna Computing Library
-----------------
Project Head: Karl Rupp rupp@iue.tuwien.ac.at
(A list of authors and contributors can be found in the manual)
License: MIT (X11), see file LICENSE in the base directory
============================================================================= */
#include <stdexcept>
#include "viennacl/forwards.h"
#include "viennacl/scalar.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/tools/tools.hpp"
#include "viennacl/linalg/cuda/common.hpp"
#include "viennacl/tools/timer.hpp"
#include "viennacl/linalg/cuda/sparse_matrix_operations_solve.hpp"
namespace viennacl
{
namespace linalg
{
namespace cuda
{
template<typename NumericT>
static inline __device__ NumericT load_and_cache(const NumericT *address)
{
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 350
return __ldg(address);
#else
return *address;
#endif
}
//
// Stage 1: Obtain upper bound for number of elements per row in C:
//
template<typename IndexT>
__device__ IndexT round_to_next_power_of_2(IndexT val)
{
if (val > 32)
return 64; // just to indicate that we need to split/factor the matrix!
else if (val > 16)
return 32;
else if (val > 8)
return 16;
else if (val > 4)
return 8;
else if (val > 2)
return 4;
else if (val > 1)
return 2;
else
return 1;
}
template<typename IndexT>
__global__ void compressed_matrix_gemm_stage_1(
const IndexT * A_row_indices,
const IndexT * A_col_indices,
IndexT A_size1,
const IndexT * B_row_indices,
IndexT *subwarpsize_per_group,
IndexT *max_nnz_row_A_per_group,
IndexT *max_nnz_row_B_per_group)
{
unsigned int subwarpsize_in_thread = 0;
unsigned int max_nnz_row_A = 0;
unsigned int max_nnz_row_B = 0;
unsigned int rows_per_group = (A_size1 - 1) / gridDim.x + 1;
unsigned int row_per_group_end = min(A_size1, rows_per_group * (blockIdx.x + 1));
for (unsigned int row = rows_per_group * blockIdx.x + threadIdx.x; row < row_per_group_end; row += blockDim.x)
{
unsigned int A_row_start = A_row_indices[row];
unsigned int A_row_end = A_row_indices[row+1];
unsigned int row_num = A_row_end - A_row_start;
subwarpsize_in_thread = max(A_row_end - A_row_start, subwarpsize_in_thread);
max_nnz_row_A = max(max_nnz_row_A, row_num);
for (unsigned int j = A_row_start; j < A_row_end; ++j)
{
unsigned int col = A_col_indices[j];
unsigned int row_len_B = B_row_indices[col + 1] - B_row_indices[col];
max_nnz_row_B = max(row_len_B, max_nnz_row_B);
}
}
// reduction to obtain maximum in thread block
__shared__ unsigned int shared_subwarpsize[256];
__shared__ unsigned int shared_max_nnz_row_A[256];
__shared__ unsigned int shared_max_nnz_row_B[256];
shared_subwarpsize[threadIdx.x] = subwarpsize_in_thread;
shared_max_nnz_row_A[threadIdx.x] = max_nnz_row_A;
shared_max_nnz_row_B[threadIdx.x] = max_nnz_row_B;
for (unsigned int stride = blockDim.x/2; stride > 0; stride /= 2)
{
__syncthreads();
if (threadIdx.x < stride)
{
shared_subwarpsize[threadIdx.x] = max( shared_subwarpsize[threadIdx.x], shared_subwarpsize[threadIdx.x + stride]);
shared_max_nnz_row_A[threadIdx.x] = max(shared_max_nnz_row_A[threadIdx.x], shared_max_nnz_row_A[threadIdx.x + stride]);
shared_max_nnz_row_B[threadIdx.x] = max(shared_max_nnz_row_B[threadIdx.x], shared_max_nnz_row_B[threadIdx.x + stride]);
}
}
if (threadIdx.x == 0)
{
subwarpsize_per_group[blockIdx.x] = round_to_next_power_of_2(shared_subwarpsize[0]);
max_nnz_row_A_per_group[blockIdx.x] = shared_max_nnz_row_A[0];
max_nnz_row_B_per_group[blockIdx.x] = shared_max_nnz_row_B[0];
}
}
//
// Stage 2: Determine sparsity pattern of C
//
// Using warp shuffle routines (CUDA arch 3.5)
template<unsigned int SubWarpSizeV, typename IndexT>
__device__ IndexT subwarp_minimum_shuffle(IndexT min_index)
{
for (unsigned int i = SubWarpSizeV/2; i >= 1; i /= 2)
min_index = min(min_index, __shfl_xor((int)min_index, (int)i));
return min_index;
}
// Using shared memory
template<unsigned int SubWarpSizeV, typename IndexT>
__device__ IndexT subwarp_minimum_shared(IndexT min_index, IndexT id_in_warp, IndexT *shared_buffer)
{
shared_buffer[threadIdx.x] = min_index;
for (unsigned int i = SubWarpSizeV/2; i >= 1; i /= 2)
shared_buffer[threadIdx.x] = min(shared_buffer[threadIdx.x], shared_buffer[(threadIdx.x + i) % 512]);
return shared_buffer[threadIdx.x - id_in_warp];
}
template<unsigned int SubWarpSizeV, typename IndexT>
__global__ void compressed_matrix_gemm_stage_2(
const IndexT * A_row_indices,
const IndexT * A_col_indices,
IndexT A_size1,
const IndexT * B_row_indices,
const IndexT * B_col_indices,
IndexT B_size2,
IndexT * C_row_indices)
{
__shared__ unsigned int shared_buffer[512];
unsigned int num_warps = blockDim.x / SubWarpSizeV;
unsigned int warp_id = threadIdx.x / SubWarpSizeV;
unsigned int id_in_warp = threadIdx.x % SubWarpSizeV;
unsigned int rows_per_group = (A_size1 - 1) / gridDim.x + 1;
unsigned int row_per_group_end = min(A_size1, rows_per_group * (blockIdx.x + 1));
for (unsigned int row = rows_per_group * blockIdx.x + warp_id; row < row_per_group_end; row += num_warps)
{
unsigned int row_A_start = A_row_indices[row];
unsigned int row_A_end = A_row_indices[row+1];
unsigned int my_row_B = row_A_start + id_in_warp;
unsigned int row_B_index = (my_row_B < row_A_end) ? A_col_indices[my_row_B] : 0;
unsigned int row_B_start = (my_row_B < row_A_end) ? load_and_cache(B_row_indices + row_B_index) : 0;
unsigned int row_B_end = (my_row_B < row_A_end) ? load_and_cache(B_row_indices + row_B_index + 1) : 0;
unsigned int num_nnz = 0;
if (row_A_end - row_A_start > 1) // zero or no row can be processed faster
{
unsigned int current_front_index = (row_B_start < row_B_end) ? load_and_cache(B_col_indices + row_B_start) : B_size2;
while (1)
{
// determine current minimum (warp shuffle)
unsigned int min_index = current_front_index;
min_index = subwarp_minimum_shared<SubWarpSizeV>(min_index, id_in_warp, shared_buffer);
if (min_index == B_size2)
break;
// update front:
if (current_front_index == min_index)
{
++row_B_start;
current_front_index = (row_B_start < row_B_end) ? load_and_cache(B_col_indices + row_B_start) : B_size2;
}
++num_nnz;
}
}
else
{
num_nnz = row_B_end - row_B_start;
}
if (id_in_warp == 0)
C_row_indices[row] = num_nnz;
}
}
//
// Stage 3: Fill C with values
//
// Using warp shuffle routines (CUDA arch 3.5)
template<unsigned int SubWarpSizeV, typename NumericT>
__device__ NumericT subwarp_accumulate_shuffle(NumericT output_value)
{
for (unsigned int i = SubWarpSizeV/2; i >= 1; i /= 2)
output_value += __shfl_xor((int)output_value, (int)i);
return output_value;
}
// Using shared memory
template<unsigned int SubWarpSizeV, typename NumericT>
__device__ NumericT subwarp_accumulate_shared(NumericT output_value, unsigned int id_in_warp, NumericT *shared_buffer)
{
shared_buffer[threadIdx.x] = output_value;
for (unsigned int i = SubWarpSizeV/2; i >= 1; i /= 2)
shared_buffer[threadIdx.x] += shared_buffer[(threadIdx.x + i) % 512];
return shared_buffer[threadIdx.x - id_in_warp];
}
template<unsigned int SubWarpSizeV, typename IndexT, typename NumericT>
__global__ void compressed_matrix_gemm_stage_3(
const IndexT * A_row_indices,
const IndexT * A_col_indices,
const NumericT * A_elements,
IndexT A_size1,
const IndexT * B_row_indices,
const IndexT * B_col_indices,
const NumericT * B_elements,
IndexT B_size2,
IndexT const * C_row_indices,
IndexT * C_col_indices,
NumericT * C_elements)
{
__shared__ unsigned int shared_indices[512];
__shared__ NumericT shared_values[512];
unsigned int num_warps = blockDim.x / SubWarpSizeV;
unsigned int warp_id = threadIdx.x / SubWarpSizeV;
unsigned int id_in_warp = threadIdx.x % SubWarpSizeV;
unsigned int rows_per_group = (A_size1 - 1) / gridDim.x + 1;
unsigned int row_per_group_end = min(A_size1, rows_per_group * (blockIdx.x + 1));
for (unsigned int row = rows_per_group * blockIdx.x + warp_id; row < row_per_group_end; row += num_warps)
{
unsigned int row_A_start = A_row_indices[row];
unsigned int row_A_end = A_row_indices[row+1];
unsigned int my_row_B = row_A_start + ((row_A_end - row_A_start > 1) ? id_in_warp : 0); // special case: single row
unsigned int row_B_index = (my_row_B < row_A_end) ? A_col_indices[my_row_B] : 0;
unsigned int row_B_start = (my_row_B < row_A_end) ? load_and_cache(B_row_indices + row_B_index) : 0;
unsigned int row_B_end = (my_row_B < row_A_end) ? load_and_cache(B_row_indices + row_B_index + 1) : 0;
NumericT val_A = (my_row_B < row_A_end) ? A_elements[my_row_B] : 0;
unsigned int index_in_C = C_row_indices[row];
if (row_A_end - row_A_start > 1)
{
unsigned int current_front_index = (row_B_start < row_B_end) ? load_and_cache(B_col_indices + row_B_start) : B_size2;
NumericT current_front_value = (row_B_start < row_B_end) ? load_and_cache(B_elements + row_B_start) : 0;
unsigned int index_buffer = 0;
NumericT value_buffer = 0;
unsigned int buffer_size = 0;
while (1)
{
// determine current minimum:
unsigned int min_index = subwarp_minimum_shared<SubWarpSizeV>(current_front_index, id_in_warp, shared_indices);
if (min_index == B_size2) // done
break;
// compute entry in C:
NumericT output_value = (current_front_index == min_index) ? val_A * current_front_value : 0;
output_value = subwarp_accumulate_shared<SubWarpSizeV>(output_value, id_in_warp, shared_values);
// update front:
if (current_front_index == min_index)
{
++row_B_start;
current_front_index = (row_B_start < row_B_end) ? load_and_cache(B_col_indices + row_B_start) : B_size2;
current_front_value = (row_B_start < row_B_end) ? load_and_cache(B_elements + row_B_start) : 0;
}
// write current front to register buffer:
index_buffer = (id_in_warp == buffer_size) ? min_index : index_buffer;
value_buffer = (id_in_warp == buffer_size) ? output_value : value_buffer;
++buffer_size;
// flush register buffer via a coalesced write once full:
if (buffer_size == SubWarpSizeV)
{
C_col_indices[index_in_C + id_in_warp] = index_buffer;
C_elements[index_in_C + id_in_warp] = value_buffer;
}
index_in_C += (buffer_size == SubWarpSizeV) ? SubWarpSizeV : 0;
buffer_size = (buffer_size == SubWarpSizeV) ? 0 : buffer_size;
}
// write remaining entries in register buffer to C:
if (id_in_warp < buffer_size)
{
C_col_indices[index_in_C + id_in_warp] = index_buffer;
C_elements[index_in_C + id_in_warp] = value_buffer;
}
}
else // write respective row using the full subwarp:
{
for (unsigned int i = row_B_start + id_in_warp; i < row_B_end; i += SubWarpSizeV)
{
C_col_indices[index_in_C + id_in_warp] = load_and_cache(B_col_indices + i);
C_elements[index_in_C + id_in_warp] = val_A * load_and_cache(B_elements + i);
index_in_C += SubWarpSizeV;
}
}
}
}
//
// Decomposition kernels:
//
template<typename IndexT>
__global__ void compressed_matrix_gemm_decompose_1(
const IndexT * A_row_indices,
IndexT A_size1,
IndexT max_per_row,
IndexT *chunks_per_row)
{
for (IndexT i = blockIdx.x * blockDim.x + threadIdx.x; i < A_size1; i += blockDim.x * gridDim.x)
{
IndexT num_entries = A_row_indices[i+1] - A_row_indices[i];
chunks_per_row[i] = (num_entries < max_per_row) ? 1 : ((num_entries - 1)/ max_per_row + 1);
}
}
template<typename IndexT, typename NumericT>
__global__ void compressed_matrix_gemm_A2(
IndexT * A2_row_indices,
IndexT * A2_col_indices,
NumericT * A2_elements,
IndexT A2_size1,
IndexT *new_row_buffer)
{
for (IndexT i = blockIdx.x * blockDim.x + threadIdx.x; i < A2_size1; i += blockDim.x * gridDim.x)
{
unsigned int index_start = new_row_buffer[i];
unsigned int index_stop = new_row_buffer[i+1];
A2_row_indices[i] = index_start;
for (IndexT j = index_start; j < index_stop; ++j)
{
A2_col_indices[j] = j;
A2_elements[j] = NumericT(1);
}
}
// write last entry in row_buffer with global thread 0:
if (threadIdx.x == 0 && blockIdx.x == 0)
A2_row_indices[A2_size1] = new_row_buffer[A2_size1];
}
template<typename IndexT, typename NumericT>
__global__ void compressed_matrix_gemm_G1(
IndexT * G1_row_indices,
IndexT * G1_col_indices,
NumericT * G1_elements,
IndexT G1_size1,
IndexT const *A_row_indices,
IndexT const *A_col_indices,
NumericT const *A_elements,
IndexT A_size1,
IndexT A_nnz,
IndexT max_per_row,
IndexT *new_row_buffer)
{
// Part 1: Copy column indices and entries:
for (IndexT i = blockIdx.x * blockDim.x + threadIdx.x; i < A_nnz; i += blockDim.x * gridDim.x)
{
G1_col_indices[i] = A_col_indices[i];
G1_elements[i] = A_elements[i];
}
// Part 2: Derive new row indicies:
for (IndexT i = blockIdx.x * blockDim.x + threadIdx.x; i < A_size1; i += blockDim.x * gridDim.x)
{
unsigned int old_start = A_row_indices[i];
unsigned int new_start = new_row_buffer[i];
unsigned int row_chunks = new_row_buffer[i+1] - new_start;
for (IndexT j=0; j<row_chunks; ++j)
G1_row_indices[new_start + j] = old_start + j * max_per_row;
}
// write last entry in row_buffer with global thread 0:
if (threadIdx.x == 0 && blockIdx.x == 0)
G1_row_indices[G1_size1] = A_row_indices[A_size1];
}
template<class NumericT, unsigned int AlignmentV>
void prod_impl(viennacl::compressed_matrix<NumericT, AlignmentV> const & A,
viennacl::compressed_matrix<NumericT, AlignmentV> const & B,
viennacl::compressed_matrix<NumericT, AlignmentV> & C)
{
C.resize(A.size1(), B.size2(), false);
unsigned int blocknum = 256;
unsigned int threadnum = 128;
viennacl::vector<unsigned int> subwarp_sizes(blocknum, viennacl::traits::context(A)); // upper bound for the nonzeros per row encountered for each work group
viennacl::vector<unsigned int> max_nnz_row_A(blocknum, viennacl::traits::context(A)); // upper bound for the nonzeros per row encountered for each work group
viennacl::vector<unsigned int> max_nnz_row_B(blocknum, viennacl::traits::context(A)); // upper bound for the nonzeros per row encountered for each work group
//
// Stage 1: Determine upper bound for number of nonzeros
//
compressed_matrix_gemm_stage_1<<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg(subwarp_sizes),
viennacl::cuda_arg(max_nnz_row_A),
viennacl::cuda_arg(max_nnz_row_B)
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_1");
subwarp_sizes.switch_memory_context(viennacl::context(MAIN_MEMORY));
unsigned int * subwarp_sizes_ptr = viennacl::linalg::host_based::detail::extract_raw_pointer<unsigned int>(subwarp_sizes.handle());
max_nnz_row_A.switch_memory_context(viennacl::context(MAIN_MEMORY));
unsigned int const * max_nnz_row_A_ptr = viennacl::linalg::host_based::detail::extract_raw_pointer<unsigned int>(max_nnz_row_A.handle());
max_nnz_row_B.switch_memory_context(viennacl::context(MAIN_MEMORY));
unsigned int const * max_nnz_row_B_ptr = viennacl::linalg::host_based::detail::extract_raw_pointer<unsigned int>(max_nnz_row_B.handle());
unsigned int max_subwarp_size = 0;
//std::cout << "Scratchpad offsets: " << std::endl;
for (std::size_t i=0; i<subwarp_sizes.size(); ++i)
max_subwarp_size = std::max(max_subwarp_size, subwarp_sizes_ptr[i]);
unsigned int A_max_nnz_per_row = 0;
for (std::size_t i=0; i<max_nnz_row_A.size(); ++i)
A_max_nnz_per_row = std::max(A_max_nnz_per_row, max_nnz_row_A_ptr[i]);
if (max_subwarp_size > 32)
{
// determine augmented size:
unsigned int max_entries_in_G = 32;
if (A_max_nnz_per_row <= 256)
max_entries_in_G = 16;
if (A_max_nnz_per_row <= 64)
max_entries_in_G = 8;
viennacl::vector<unsigned int> exclusive_scan_helper(A.size1() + 1, viennacl::traits::context(A));
compressed_matrix_gemm_decompose_1<<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
static_cast<unsigned int>(A.size1()),
static_cast<unsigned int>(max_entries_in_G),
viennacl::cuda_arg(exclusive_scan_helper)
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_decompose_1");
viennacl::linalg::exclusive_scan(exclusive_scan_helper);
unsigned int augmented_size = exclusive_scan_helper[A.size1()];
// split A = A2 * G1
viennacl::compressed_matrix<NumericT, AlignmentV> A2(A.size1(), augmented_size, augmented_size, viennacl::traits::context(A));
viennacl::compressed_matrix<NumericT, AlignmentV> G1(augmented_size, A.size2(), A.nnz(), viennacl::traits::context(A));
// fill A2:
compressed_matrix_gemm_A2<<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A2.handle1()),
viennacl::cuda_arg<unsigned int>(A2.handle2()),
viennacl::cuda_arg<NumericT>(A2.handle()),
static_cast<unsigned int>(A2.size1()),
viennacl::cuda_arg(exclusive_scan_helper)
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_A2");
// fill G1:
compressed_matrix_gemm_G1<<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(G1.handle1()),
viennacl::cuda_arg<unsigned int>(G1.handle2()),
viennacl::cuda_arg<NumericT>(G1.handle()),
static_cast<unsigned int>(G1.size1()),
viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
viennacl::cuda_arg<NumericT>(A.handle()),
static_cast<unsigned int>(A.size1()),
static_cast<unsigned int>(A.nnz()),
static_cast<unsigned int>(max_entries_in_G),
viennacl::cuda_arg(exclusive_scan_helper)
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_G1");
// compute tmp = G1 * B;
// C = A2 * tmp;
viennacl::compressed_matrix<NumericT, AlignmentV> tmp(G1.size1(), B.size2(), 0, viennacl::traits::context(A));
prod_impl(G1, B, tmp); // this runs a standard RMerge without decomposition of G1
prod_impl(A2, tmp, C); // this may split A2 again
return;
}
//std::cout << "Running RMerge with subwarp size " << max_subwarp_size << std::endl;
subwarp_sizes.switch_memory_context(viennacl::traits::context(A));
max_nnz_row_A.switch_memory_context(viennacl::traits::context(A));
max_nnz_row_B.switch_memory_context(viennacl::traits::context(A));
//
// Stage 2: Determine pattern of C
//
if (max_subwarp_size == 32)
{
compressed_matrix_gemm_stage_2<32><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_2");
}
else if (max_subwarp_size == 16)
{
compressed_matrix_gemm_stage_2<16><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_2");
}
else
{
compressed_matrix_gemm_stage_2<8><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_2");
}
// exclusive scan on C.handle1(), ultimately allowing to allocate remaining memory for C
viennacl::backend::typesafe_host_array<unsigned int> row_buffer(C.handle1(), C.size1() + 1);
viennacl::backend::memory_read(C.handle1(), 0, row_buffer.raw_size(), row_buffer.get());
unsigned int current_offset = 0;
for (std::size_t i=0; i<C.size1(); ++i)
{
unsigned int tmp = row_buffer[i];
row_buffer.set(i, current_offset);
current_offset += tmp;
}
row_buffer.set(C.size1(), current_offset);
viennacl::backend::memory_write(C.handle1(), 0, row_buffer.raw_size(), row_buffer.get());
//
// Stage 3: Compute entries in C
//
C.reserve(current_offset, false);
if (max_subwarp_size == 32)
{
compressed_matrix_gemm_stage_3<32><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
viennacl::cuda_arg<NumericT>(A.handle()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
viennacl::cuda_arg<NumericT>(B.handle()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1()),
viennacl::cuda_arg<unsigned int>(C.handle2()),
viennacl::cuda_arg<NumericT>(C.handle())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_3");
}
else if (max_subwarp_size == 16)
{
compressed_matrix_gemm_stage_3<16><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
viennacl::cuda_arg<NumericT>(A.handle()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
viennacl::cuda_arg<NumericT>(B.handle()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1()),
viennacl::cuda_arg<unsigned int>(C.handle2()),
viennacl::cuda_arg<NumericT>(C.handle())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_3");
}
else
{
compressed_matrix_gemm_stage_3<8><<<blocknum, threadnum>>>(viennacl::cuda_arg<unsigned int>(A.handle1()),
viennacl::cuda_arg<unsigned int>(A.handle2()),
viennacl::cuda_arg<NumericT>(A.handle()),
static_cast<unsigned int>(A.size1()),
viennacl::cuda_arg<unsigned int>(B.handle1()),
viennacl::cuda_arg<unsigned int>(B.handle2()),
viennacl::cuda_arg<NumericT>(B.handle()),
static_cast<unsigned int>(B.size2()),
viennacl::cuda_arg<unsigned int>(C.handle1()),
viennacl::cuda_arg<unsigned int>(C.handle2()),
viennacl::cuda_arg<NumericT>(C.handle())
);
VIENNACL_CUDA_LAST_ERROR_CHECK("compressed_matrix_gemm_stage_3");
}
}
} // namespace cuda
} //namespace linalg
} //namespace viennacl
#endif
viennacl::compressed_matrix::size2
const vcl_size_t & size2() const
Returns the number of columns.
Definition: compressed_matrix.hpp:929
viennacl::backend::typesafe_host_array
Helper class implementing an array on the host. Default case: No conversion necessary.
Definition: util.hpp:92
viennacl::backend::memory_write
void memory_write(mem_handle &dst_buffer, vcl_size_t dst_offset, vcl_size_t bytes_to_write, const void *ptr, bool async=false)
Writes data from main RAM identified by 'ptr' to the buffer identified by 'dst_buffer'.
Definition: memory.hpp:220
viennacl::linalg::cuda::subwarp_minimum_shared
__device__ IndexT subwarp_minimum_shared(IndexT min_index, IndexT id_in_warp, IndexT *shared_buffer)
Definition: spgemm_rmerge.hpp:152
viennacl::compressed_matrix::size1
const vcl_size_t & size1() const
Returns the number of rows.
Definition: compressed_matrix.hpp:927
viennacl::linalg::cuda::compressed_matrix_gemm_decompose_1
__global__ void compressed_matrix_gemm_decompose_1(const IndexT *A_row_indices, IndexT A_size1, IndexT max_per_row, IndexT *chunks_per_row)
Definition: spgemm.hpp:469
tools.hpp
Various little tools used here and there in ViennaCL.
viennacl::traits::stride
result_of::size_type< viennacl::vector_base< T > >::type stride(viennacl::vector_base< T > const &s)
Definition: stride.hpp:45
forwards.h
This file provides the forward declarations for the main types used within ViennaCL.
viennacl::backend::memory_read
void memory_read(mem_handle const &src_buffer, vcl_size_t src_offset, vcl_size_t bytes_to_read, void *ptr, bool async=false)
Reads data from a buffer back to main RAM.
Definition: memory.hpp:261
viennacl::linalg::detail::max
T max(const T &lhs, const T &rhs)
Maximum.
Definition: util.hpp:59
viennacl::linalg::cuda::prod_impl
void prod_impl(const matrix_base< NumericT > &mat, bool mat_transpose, const vector_base< NumericT > &vec, vector_base< NumericT > &result)
Carries out matrix-vector multiplication.
Definition: matrix_operations.hpp:1464
viennacl::compressed_matrix::handle
const handle_type & handle() const
Returns the OpenCL handle to the matrix entry array.
Definition: compressed_matrix.hpp:942
viennacl::compressed_matrix::handle1
const handle_type & handle1() const
Returns the OpenCL handle to the row index array.
Definition: compressed_matrix.hpp:936
viennacl::compressed_matrix::nnz
const vcl_size_t & nnz() const
Returns the number of nonzero entries.
Definition: compressed_matrix.hpp:931
viennacl::linalg::cuda::compressed_matrix_gemm_stage_3
__global__ void compressed_matrix_gemm_stage_3(const IndexT *A_row_indices, const IndexT *A_col_indices, const NumericT *A_elements, IndexT A_size1, const IndexT *B_row_indices, const IndexT *B_col_indices, const NumericT *B_elements, IndexT B_size2, IndexT const *C_row_indices, IndexT *C_col_indices, NumericT *C_elements, unsigned int *subwarpsize_array, unsigned int *max_row_size_A, unsigned int *max_row_size_B, unsigned int *scratchpad_offsets, unsigned int *scratchpad_indices, NumericT *scratchpad_values)
Definition: spgemm.hpp:365
NumericT
float NumericT
Definition: bisect.cpp:40
viennacl::context
Represents a generic 'context' similar to an OpenCL context, but is backend-agnostic and thus also su...
Definition: context.hpp:39
viennacl::linalg::cuda::round_to_next_power_of_2
__device__ IndexT round_to_next_power_of_2(IndexT val)
Definition: spgemm.hpp:63
viennacl::linalg::cuda::compressed_matrix_gemm_stage_1
__global__ void compressed_matrix_gemm_stage_1(const IndexT *A_row_indices, const IndexT *A_col_indices, IndexT A_size1, const IndexT *B_row_indices, IndexT *subwarpsize_per_group, IndexT *max_nnz_row_A_per_group, IndexT *max_nnz_row_B_per_group)
Definition: spgemm.hpp:82
viennacl::linalg::cuda::subwarp_minimum_shuffle
__device__ IndexT subwarp_minimum_shuffle(IndexT min_index)
Definition: spgemm_rmerge.hpp:143
viennacl::linalg::cuda::compressed_matrix_gemm_A2
__global__ void compressed_matrix_gemm_A2(IndexT *A2_row_indices, IndexT *A2_col_indices, NumericT *A2_elements, IndexT A2_size1, IndexT *new_row_buffer)
Definition: spgemm.hpp:484
viennacl::linalg::cuda::subwarp_accumulate_shared
__device__ NumericT subwarp_accumulate_shared(NumericT output_value, unsigned int id_in_warp, NumericT *shared_buffer)
Definition: spgemm_rmerge.hpp:241
viennacl::compressed_matrix::handle2
const handle_type & handle2() const
Returns the OpenCL handle to the column index array.
Definition: compressed_matrix.hpp:938
viennacl::vector< unsigned int >
viennacl::row
vector_expression< const matrix_base< NumericT, F >, const unsigned int, op_row > row(const matrix_base< NumericT, F > &A, unsigned int i)
Definition: matrix.hpp:910
sparse_matrix_operations_solve.hpp
Implementations of direct triangular solvers for sparse matrices using CUDA.
viennacl::MAIN_MEMORY
Definition: forwards.h:348
viennacl::compressed_matrix::reserve
void reserve(vcl_size_t new_nonzeros, bool preserve=true)
Allocate memory for the supplied number of nonzeros in the matrix. Old values are preserved...
Definition: compressed_matrix.hpp:794
timer.hpp
A simple, yet (mostly) sufficiently accurate timer for benchmarking and profiling.
viennacl::traits::context
viennacl::context context(T const &t)
Returns an ID for the currently active memory domain of an object.
Definition: context.hpp:40
common.hpp
Common routines for CUDA execution.
vector.hpp
The vector type with operator-overloads and proxy classes is defined here. Linear algebra operations ...
viennacl::linalg::max
NumericT max(std::vector< NumericT > const &v1)
Definition: maxmin.hpp:47
viennacl::backend::typesafe_host_array::set
void set(vcl_size_t index, U value)
Definition: util.hpp:115
viennacl::linalg::cuda::compressed_matrix_gemm_G1
__global__ void compressed_matrix_gemm_G1(IndexT *G1_row_indices, IndexT *G1_col_indices, NumericT *G1_elements, IndexT G1_size1, IndexT const *A_row_indices, IndexT const *A_col_indices, NumericT const *A_elements, IndexT A_size1, IndexT A_nnz, IndexT max_per_row, IndexT *new_row_buffer)
Definition: spgemm.hpp:511
viennacl::backend::mem_handle::raw_size
vcl_size_t raw_size() const
Returns the number of bytes of the currently active buffer.
Definition: mem_handle.hpp:230
viennacl::compressed_matrix
A sparse square matrix in compressed sparse rows format.
Definition: compressed_matrix.hpp:559
viennacl::linalg::exclusive_scan
void exclusive_scan(vector_base< NumericT > &vec1, vector_base< NumericT > &vec2)
This function implements an exclusive scan.
Definition: vector_operations.hpp:1240
VIENNACL_CUDA_LAST_ERROR_CHECK
#define VIENNACL_CUDA_LAST_ERROR_CHECK(message)
Definition: common.hpp:30
viennacl::cuda_arg
NumericT * cuda_arg(scalar< NumericT > &obj)
Convenience helper function for extracting the CUDA handle from a ViennaCL scalar. Non-const version.
Definition: common.hpp:39
scalar.hpp
Implementation of the ViennaCL scalar class.
viennacl::compressed_matrix::resize
void resize(vcl_size_t new_size1, vcl_size_t new_size2, bool preserve=true)
Resize the matrix.
Definition: compressed_matrix.hpp:829
viennacl::linalg::cuda::subwarp_accumulate_shuffle
__device__ NumericT subwarp_accumulate_shuffle(NumericT output_value)
Definition: spgemm_rmerge.hpp:232
viennacl::linalg::cuda::compressed_matrix_gemm_stage_2
__global__ void compressed_matrix_gemm_stage_2(const IndexT *A_row_indices, const IndexT *A_col_indices, IndexT A_size1, const IndexT *B_row_indices, const IndexT *B_col_indices, IndexT B_size2, IndexT *C_row_indices, unsigned int *subwarpsize_array, unsigned int *max_row_size_A, unsigned int *max_row_size_B, unsigned int *scratchpad_offsets, unsigned int *scratchpad_indices)
Definition: spgemm.hpp:217
viennacl::linalg::min
NumericT min(std::vector< NumericT > const &v1)
Definition: maxmin.hpp:91