CUTLASS 2.0 (#62)
CUTLASS 2.0 Substantially refactored for - Better performance, particularly for native Turing Tensor Cores - Robust and durable templates spanning the design space - Encapsulated functionality embodying modern C++11 programming techniques - Optimized containers and data types for efficient, generic, portable device code Updates to: - Quick start guide - Documentation - Utilities - CUTLASS Profiler Native Turing Tensor Cores - Efficient GEMM kernels targeting Turing Tensor Cores - Mixed-precision floating point, 8-bit integer, 4-bit integer, and binarized operands Coverage of existing CUTLASS functionality: - GEMM kernels targeting CUDA and Tensor Cores in NVIDIA GPUs - Volta Tensor Cores through native mma.sync and through WMMA API - Optimizations such as parallel reductions, threadblock rasterization, and intra-threadblock reductions - Batched GEMM operations - Complex-valued GEMMs Note: this commit and all that follow require a host compiler supporting C++11 or greater.
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are not permitted.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Utility for parsing command line arguments
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
/******************************************************************************
|
||||
* command_line
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility for parsing command line arguments
|
||||
*/
|
||||
struct CommandLine {
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> args;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
CommandLine(int argc, const char** argv) {
|
||||
using namespace std;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string arg = argv[i];
|
||||
|
||||
if ((arg[0] != '-') || (arg[1] != '-')) {
|
||||
args.push_back(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
string::size_type pos;
|
||||
string key, val;
|
||||
if ((pos = arg.find('=')) == string::npos) {
|
||||
key = string(arg, 2, arg.length() - 2);
|
||||
val = "";
|
||||
} else {
|
||||
key = string(arg, 2, pos - 2);
|
||||
val = string(arg, pos + 1, arg.length() - 1);
|
||||
}
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a flag "--<flag>" is present in the commandline
|
||||
*/
|
||||
bool check_cmd_line_flag(const char* arg_name) const {
|
||||
using namespace std;
|
||||
|
||||
for (int i = 0; i < int(keys.size()); ++i) {
|
||||
if (keys[i] == string(arg_name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of naked (non-flag and non-key-value) commandline parameters
|
||||
*/
|
||||
template <typename value_t>
|
||||
int num_naked_args() const {
|
||||
return args.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the commandline parameter for a given index (not including flags)
|
||||
*/
|
||||
template <typename value_t>
|
||||
void get_cmd_line_argument(int index, value_t& val) const {
|
||||
using namespace std;
|
||||
if (index < args.size()) {
|
||||
istringstream str_stream(args[index]);
|
||||
str_stream >> val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the boolean value specified for a given commandline parameter --<flag>=<bool>
|
||||
*/
|
||||
void get_cmd_line_argument(const char* arg_name, bool& val, bool _default = true) const {
|
||||
val = _default;
|
||||
if (check_cmd_line_flag(arg_name)) {
|
||||
std::string value;
|
||||
get_cmd_line_argument(arg_name, value);
|
||||
|
||||
val = !(value == "0" || value == "false");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the value specified for a given commandline parameter --<flag>=<value>
|
||||
*/
|
||||
template <typename value_t>
|
||||
void get_cmd_line_argument(const char* arg_name,
|
||||
value_t& val,
|
||||
value_t const& _default = value_t()) const {
|
||||
using namespace std;
|
||||
|
||||
val = _default;
|
||||
|
||||
for (int i = 0; i < int(keys.size()); ++i) {
|
||||
if (keys[i] == string(arg_name)) {
|
||||
istringstream str_stream(values[i]);
|
||||
str_stream >> val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values specified for a given commandline parameter --<flag>=<value>,<value>*
|
||||
*/
|
||||
template <typename value_t>
|
||||
void get_cmd_line_arguments(const char* arg_name,
|
||||
std::vector<value_t>& vals,
|
||||
char sep = ',') const {
|
||||
using namespace std;
|
||||
|
||||
if (check_cmd_line_flag(arg_name)) {
|
||||
// Clear any default values
|
||||
vals.clear();
|
||||
|
||||
// Recover from multi-value string
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
if (keys[i] == string(arg_name)) {
|
||||
string val_string(values[i]);
|
||||
seperate_string(val_string, vals, sep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values specified for a given commandline parameter
|
||||
* --<flag>=<value>,<value_start:value_end>*
|
||||
*/
|
||||
void get_cmd_line_argument_pairs(const char* arg_name,
|
||||
std::vector<std::pair<std::string, std::string> >& tokens,
|
||||
char delim = ',',
|
||||
char sep = ':') const {
|
||||
if (check_cmd_line_flag(arg_name)) {
|
||||
std::string value;
|
||||
get_cmd_line_argument(arg_name, value);
|
||||
|
||||
tokenize(tokens, value, delim, sep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ranges specified for a given commandline parameter
|
||||
* --<flag>=<key:value>,<key:value>*
|
||||
*/
|
||||
void get_cmd_line_argument_ranges(const char* arg_name,
|
||||
std::vector<std::vector<std::string> >& vals,
|
||||
char delim = ',',
|
||||
char sep = ':') const {
|
||||
std::vector<std::string> ranges;
|
||||
get_cmd_line_arguments(arg_name, ranges, delim);
|
||||
|
||||
for (std::vector<std::string>::const_iterator range = ranges.begin();
|
||||
range != ranges.end(); ++range) {
|
||||
|
||||
std::vector<std::string> range_vals;
|
||||
seperate_string(*range, range_vals, sep);
|
||||
vals.push_back(range_vals);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of pairs parsed
|
||||
*/
|
||||
int parsed_argc() const { return (int)keys.size(); }
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// Tokenizes a comma-delimited list of string pairs delimited by ':'
|
||||
static void tokenize(std::vector<std::pair<std::string, std::string> >& tokens,
|
||||
std::string const& str,
|
||||
char delim = ',',
|
||||
char sep = ':') {
|
||||
// Home-built to avoid Boost dependency
|
||||
size_t s_idx = 0;
|
||||
size_t d_idx = std::string::npos;
|
||||
while (s_idx < str.size()) {
|
||||
d_idx = str.find_first_of(delim, s_idx);
|
||||
|
||||
size_t end_idx = (d_idx != std::string::npos ? d_idx : str.size());
|
||||
size_t sep_idx = str.find_first_of(sep, s_idx);
|
||||
size_t offset = 1;
|
||||
if (sep_idx == std::string::npos || sep_idx >= end_idx) {
|
||||
sep_idx = end_idx;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> item(
|
||||
str.substr(s_idx, sep_idx - s_idx),
|
||||
str.substr(sep_idx + offset, end_idx - sep_idx - offset));
|
||||
|
||||
tokens.push_back(item);
|
||||
s_idx = end_idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Tokenizes a comma-delimited list of string pairs delimited by ':'
|
||||
static void tokenize(std::vector<std::string>& tokens,
|
||||
std::string const& str,
|
||||
char delim = ',',
|
||||
char sep = ':') {
|
||||
typedef std::vector<std::pair<std::string, std::string> > TokenVector;
|
||||
typedef TokenVector::const_iterator token_iterator;
|
||||
|
||||
std::vector<std::pair<std::string, std::string> > token_pairs;
|
||||
tokenize(token_pairs, str, delim, sep);
|
||||
for (token_iterator tok = token_pairs.begin(); tok != token_pairs.end(); ++tok) {
|
||||
tokens.push_back(tok->first);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename value_t>
|
||||
static void seperate_string(std::string const& str,
|
||||
std::vector<value_t>& vals,
|
||||
char sep = ',') {
|
||||
std::istringstream str_stream(str);
|
||||
std::string::size_type old_pos = 0;
|
||||
std::string::size_type new_pos = 0;
|
||||
|
||||
// Iterate <sep>-delimited values
|
||||
value_t val;
|
||||
while ((new_pos = str.find(sep, old_pos)) != std::string::npos) {
|
||||
if (new_pos != old_pos) {
|
||||
str_stream.width(new_pos - old_pos);
|
||||
str_stream >> val;
|
||||
vals.push_back(val);
|
||||
}
|
||||
|
||||
// skip over delimiter
|
||||
str_stream.ignore(1);
|
||||
old_pos = new_pos + 1;
|
||||
}
|
||||
|
||||
// Read last value
|
||||
str_stream >> val;
|
||||
vals.push_back(val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,136 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \file
|
||||
\brief Contains code for debugging cutlass code
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device_dump.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/******************************************************************************
|
||||
* Debug and logging macros
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Formats and prints the given message to stdout
|
||||
*/
|
||||
#if !defined(CUDA_LOG)
|
||||
#if !defined(__CUDA_ARCH__)
|
||||
#define CUDA_LOG(format, ...) printf(format, __VA_ARGS__)
|
||||
#else
|
||||
#define CUDA_LOG(format, ...) \
|
||||
printf("[block (%d,%d,%d), thread (%d,%d,%d)]: " format, \
|
||||
blockIdx.x, \
|
||||
blockIdx.y, \
|
||||
blockIdx.z, \
|
||||
threadIdx.x, \
|
||||
threadIdx.y, \
|
||||
threadIdx.z, \
|
||||
__VA_ARGS__);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Formats and prints the given message to stdout only if DEBUG is defined
|
||||
*/
|
||||
#if !defined(CUDA_LOG_DEBUG)
|
||||
#ifdef DEBUG
|
||||
#define CUDA_LOG_DEBUG(format, ...) CUDA_LOG(format, __VA_ARGS__)
|
||||
#else
|
||||
#define CUDA_LOG_DEBUG(format, ...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief The corresponding error message is printed to \p stderr (or \p stdout in device code)
|
||||
* along with the supplied source context.
|
||||
*
|
||||
* \return The CUDA error.
|
||||
*/
|
||||
__host__ CUTLASS_DEVICE cudaError_t cuda_perror_impl(cudaError_t error,
|
||||
const char* filename,
|
||||
int line) {
|
||||
(void)filename;
|
||||
(void)line;
|
||||
if (error) {
|
||||
#if !defined(__CUDA_ARCH__)
|
||||
fprintf(
|
||||
stderr, "CUDA error %d [%s, %d]: %s\n", error, filename, line, cudaGetErrorString(error));
|
||||
fflush(stderr);
|
||||
#else
|
||||
printf("CUDA error %d [%s, %d]\n", error, filename, line);
|
||||
#endif
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Perror macro
|
||||
*/
|
||||
#ifndef CUDA_PERROR
|
||||
#define CUDA_PERROR(e) cuda_perror_impl((cudaError_t)(e), __FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Perror macro with exit
|
||||
*/
|
||||
#ifndef CUDA_PERROR_EXIT
|
||||
#define CUDA_PERROR_EXIT(e) \
|
||||
if (cuda_perror_impl((cudaError_t)(e), __FILE__, __LINE__)) { \
|
||||
exit(1); \
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Perror macro only if DEBUG is defined
|
||||
*/
|
||||
#ifndef CUDA_PERROR_DEBUG
|
||||
#ifdef DEBUG
|
||||
#define CUDA_PERROR_DEBUG(e) CUDA_PERROR(e)
|
||||
#else
|
||||
#define CUDA_PERROR_DEBUG(e) (e)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A small helper class to dump a type at compile time
|
||||
// Usage:: DumpType<Class>::Class
|
||||
template <typename T>
|
||||
struct DebugType {};
|
||||
|
||||
template <typename T>
|
||||
void DebugTypeFunc(T const& t) {
|
||||
T::t;
|
||||
}
|
||||
|
||||
// A small helper class to dump a compile time constant at compile time
|
||||
// Usage: DumpValue<Class::kConstant>::kConstant
|
||||
template <int Value>
|
||||
struct DebugValue {};
|
||||
@@ -0,0 +1,181 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "cutlass/cutlass.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ interface to dump fragments and shared memory contents for
|
||||
* debugging.
|
||||
*/
|
||||
|
||||
namespace cutlass {
|
||||
namespace debug {
|
||||
|
||||
/******************************************************************************
|
||||
* Dump the fragments
|
||||
******************************************************************************/
|
||||
|
||||
/// The first N threads dump the first M elements from their fragments with a
|
||||
/// stride of S elements. If N is not specified, dump the data of all the
|
||||
/// threads. If M is not specified, dump all the elements of the fragment.
|
||||
template <typename Fragment>
|
||||
CUTLASS_DEVICE void dump_fragment(Fragment const& frag, int N = 0, int M = 0,
|
||||
int S = 1) {
|
||||
int total_threads = blockDim.x * blockDim.y * blockDim.z;
|
||||
int block_id =
|
||||
blockIdx.x + blockIdx.y * gridDim.x + gridDim.x * gridDim.y * blockIdx.z;
|
||||
int thread_id = (threadIdx.z * (blockDim.x * blockDim.y)) +
|
||||
(threadIdx.y * blockDim.x) + threadIdx.x;
|
||||
|
||||
if (N < 0 || N > total_threads) {
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("Thread number N = %d should between [1, %d].\n", N,
|
||||
total_threads);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int total_elements = frag.size();
|
||||
|
||||
if (M < 0 || M > total_elements) {
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("Element number M = %d should between [1, %d].\n", M,
|
||||
total_elements);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (N == 0) N = total_threads;
|
||||
|
||||
if (M == 0) M = total_elements;
|
||||
|
||||
if (S < 1 || S > M) {
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("Stride S = %d should between [1, %d].\n", S, M);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("\n*******************Dumping the fragments*******************\n\n");
|
||||
|
||||
CUTLASS_PRAGMA_NO_UNROLL
|
||||
for (int tid = 0; tid < N; ++tid) {
|
||||
if (tid == thread_id) {
|
||||
printf("TB%d W%d T%d: ", block_id, tid / 32, tid & 31);
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < M; i += S) {
|
||||
printf("%.0f ", float(typename Fragment::value_type(frag[i])));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("\n***********************************************************\n\n");
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Dump the shared memory
|
||||
******************************************************************************/
|
||||
|
||||
#define SHMEM_ROW_SIZE 128
|
||||
|
||||
/// Dump the shared memory contents. ptr is the begin address, size specifies
|
||||
/// the number of elements that need to be dumped, and S specifies the stride.
|
||||
template <typename Element>
|
||||
CUTLASS_DEVICE void dump_shmem(Element const* ptr, size_t size, int S = 1) {
|
||||
int block_id =
|
||||
blockIdx.x + blockIdx.y * gridDim.x + gridDim.x * gridDim.y * blockIdx.z;
|
||||
int thread_id = (threadIdx.z * (blockDim.x * blockDim.y)) +
|
||||
(threadIdx.y * blockDim.x) + threadIdx.x;
|
||||
|
||||
if (ptr == nullptr) {
|
||||
if (thread_id == 0 && block_id == 0) printf("ptr is null.\n");
|
||||
|
||||
__syncthreads();
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < 1) {
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("Element size is less than 1\n");
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int row_elements = SHMEM_ROW_SIZE / sizeof(Element);
|
||||
|
||||
if (S < 1 || S > row_elements) {
|
||||
if (thread_id == 0 && block_id == 0)
|
||||
printf("Stride S = %d should between [1, %d].\n", S, row_elements);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (thread_id == 0)
|
||||
printf("\n********Dumping the shared memory of TB %d*******\n\n", block_id);
|
||||
|
||||
if (thread_id == 0) {
|
||||
for (int i = 0; i < size; i += row_elements) {
|
||||
for (int j = 0; j < row_elements; j += S) {
|
||||
printf("%.0f ", float(ptr[i + j]));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (thread_id == 0)
|
||||
printf("\n***********************************************************\n\n");
|
||||
|
||||
__syncthreads();
|
||||
|
||||
return;
|
||||
}
|
||||
} // namespace debug
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,203 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are not permitted.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ interface to CUDA device memory management functions.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "cutlass/platform/platform.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace device_memory {
|
||||
|
||||
/******************************************************************************
|
||||
* Allocation lifetime
|
||||
******************************************************************************/
|
||||
|
||||
/// Allocate a buffer of \p count elements of type \p T on the current CUDA device
|
||||
template <typename T>
|
||||
T* allocate(size_t count = 1) {
|
||||
T* ptr = 0;
|
||||
size_t bytes = sizeof(T) * count;
|
||||
|
||||
cudaError_t cuda_error = cudaMalloc((void**)&ptr, bytes);
|
||||
if (cuda_error != cudaSuccess) {
|
||||
throw cuda_exception("Failed to allocate memory", cuda_error);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Free the buffer pointed to by \p ptr
|
||||
template <typename T>
|
||||
void free(T* ptr) {
|
||||
if (ptr) {
|
||||
cudaError_t cuda_error = (cudaFree(ptr));
|
||||
if (cuda_error != cudaSuccess) {
|
||||
throw cuda_exception("Failed to free device memory", cuda_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Data movement
|
||||
******************************************************************************/
|
||||
|
||||
template <typename T>
|
||||
void copy(T* dst, T const* src, size_t count, cudaMemcpyKind kind) {
|
||||
size_t bytes = count * sizeof_bits<T>::value / 8;
|
||||
if (bytes == 0 && count > 0)
|
||||
bytes = 1;
|
||||
cudaError_t cuda_error = (cudaMemcpy(dst, src, bytes, kind));
|
||||
if (cuda_error != cudaSuccess) {
|
||||
throw cuda_exception("cudaMemcpy() failed", cuda_error);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copy_to_device(T* dst, T const* src, size_t count = 1) {
|
||||
copy(dst, src, count, cudaMemcpyHostToDevice);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copy_to_host(T* dst, T const* src, size_t count = 1) {
|
||||
copy(dst, src, count, cudaMemcpyDeviceToHost);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copy_device_to_device(T* dst, T const* src, size_t count = 1) {
|
||||
copy(dst, src, count, cudaMemcpyDeviceToDevice);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copy_host_to_host(T* dst, T const* src, size_t count = 1) {
|
||||
copy(dst, src, count, cudaMemcpyHostToHost);
|
||||
}
|
||||
|
||||
/// Copies elements from device memory to host-side range
|
||||
template <typename OutputIterator, typename T>
|
||||
void insert_to_host(OutputIterator begin, OutputIterator end, T const* device_begin) {
|
||||
size_t elements = end - begin;
|
||||
copy_to_host(&*begin, device_begin, elements);
|
||||
}
|
||||
|
||||
/// Copies elements to device memory from host-side range
|
||||
template <typename T, typename InputIterator>
|
||||
void insert_to_device(T* device_begin, InputIterator begin, InputIterator end) {
|
||||
size_t elements = end - begin;
|
||||
copy_to_device(device_begin, &*begin, elements);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* "Smart" device memory allocation
|
||||
******************************************************************************/
|
||||
|
||||
/// Device allocation abstraction that tracks size and capacity
|
||||
template <typename T>
|
||||
struct allocation {
|
||||
/// Delete functor for CUDA device memory
|
||||
struct deleter {
|
||||
void operator()(T* ptr) {
|
||||
cudaError_t cuda_error = (cudaFree(ptr));
|
||||
if (cuda_error != cudaSuccess) {
|
||||
// noexcept
|
||||
// throw cuda_exception("cudaFree() failed", cuda_error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Number of elements of T allocated on the current CUDA device
|
||||
size_t capacity;
|
||||
|
||||
/// Smart pointer
|
||||
platform::unique_ptr<T, deleter> smart_ptr;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor: allocates no memory
|
||||
allocation() : capacity(0) {}
|
||||
|
||||
/// Constructor: allocates \p capacity elements on the current CUDA device
|
||||
allocation(size_t _capacity) : smart_ptr(allocate<T>(_capacity)), capacity(_capacity) {}
|
||||
|
||||
/// Copy constructor
|
||||
allocation(allocation const &p): smart_ptr(allocate<T>(p.capacity)), capacity(p.capacity) {
|
||||
copy_device_to_device(smart_ptr.get(), p.get(), capacity);
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
~allocation() { reset(); }
|
||||
|
||||
/// Returns a pointer to the managed object
|
||||
T* get() const { return smart_ptr.get(); }
|
||||
|
||||
/// Releases the ownership of the managed object (without deleting) and resets capacity to zero
|
||||
T* release() {
|
||||
capacity = 0;
|
||||
return smart_ptr.release();
|
||||
}
|
||||
|
||||
/// Deletes the managed object and resets capacity to zero
|
||||
void reset() {
|
||||
capacity = 0;
|
||||
smart_ptr.reset();
|
||||
}
|
||||
|
||||
/// Deletes managed object, if owned, and replaces its reference with a given pointer and capacity
|
||||
void reset(T* _ptr, size_t _capacity) {
|
||||
smart_ptr.reset(_ptr);
|
||||
capacity = _capacity;
|
||||
}
|
||||
|
||||
/// Returns a pointer to the object owned by *this
|
||||
T* operator->() const { return smart_ptr.get(); }
|
||||
|
||||
/// Returns the deleter object which would be used for destruction of the managed object.
|
||||
deleter& get_deleter() { return smart_ptr.get_deleter(); }
|
||||
|
||||
/// Returns the deleter object which would be used for destruction of the managed object (const)
|
||||
const deleter& get_deleter() const { return smart_ptr.get_deleter(); }
|
||||
|
||||
/// Copies a device-side memory allocation
|
||||
allocation & operator=(allocation const &p) {
|
||||
if (capacity != p.capacity) {
|
||||
smart_ptr.reset(allocate<T>(p.capacity));
|
||||
capacity = p.capacity;
|
||||
}
|
||||
copy_device_to_device(smart_ptr.get(), p.get(), capacity);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace device_memory
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,137 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
/*! \file
|
||||
\brief This header contains a class to parametrize a statistical distribution function.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Distribution type
|
||||
struct Distribution {
|
||||
/// Variant types
|
||||
enum Kind { Invalid, Uniform, Gaussian, Identity, Sequential };
|
||||
|
||||
/// Distribution state
|
||||
union {
|
||||
/// Uniform distribution
|
||||
struct {
|
||||
double min;
|
||||
double max;
|
||||
} uniform;
|
||||
|
||||
/// Gaussian distribution
|
||||
struct {
|
||||
double mean;
|
||||
double stddev;
|
||||
} gaussian;
|
||||
|
||||
/// Elements are linear combination of row and column index
|
||||
struct {
|
||||
double start;
|
||||
double delta;
|
||||
} sequential;
|
||||
};
|
||||
|
||||
/// Active variant kind
|
||||
Kind kind;
|
||||
|
||||
/// Random values are cast to integer after scaling by this power of two
|
||||
int int_scale;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Distribution() : kind(Invalid), int_scale(0) {}
|
||||
|
||||
/// Configures distribution as uniform random
|
||||
Distribution &set_uniform(double _min, double _max, int _int_scale = 0) {
|
||||
kind = Uniform;
|
||||
uniform.min = _min;
|
||||
uniform.max = _max;
|
||||
int_scale = _int_scale;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Configures distribution as Gaussian distribution
|
||||
Distribution &set_gaussian(double _mean, double _stddev, int _int_scale = 0) {
|
||||
kind = Gaussian;
|
||||
gaussian.mean = _mean;
|
||||
gaussian.stddev = _stddev;
|
||||
int_scale = _int_scale;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Sets identity
|
||||
Distribution &set_identity() {
|
||||
kind = Identity;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Sets sequential
|
||||
Distribution &set_sequential(double start, double delta, int _int_scale = 0) {
|
||||
kind = Sequential;
|
||||
sequential.start = start;
|
||||
sequential.delta = delta;
|
||||
int_scale = _int_scale;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cutlass
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Prints a Distribution to ostream
|
||||
inline std::ostream &operator<<(std::ostream &out, cutlass::Distribution const &dist) {
|
||||
switch (dist.kind) {
|
||||
case cutlass::Distribution::Uniform:
|
||||
out << "uniform, min: " << dist.uniform.min << ", max: " << dist.uniform.max;
|
||||
break;
|
||||
case cutlass::Distribution::Gaussian:
|
||||
out << "gaussian, mean: " << dist.gaussian.mean << ", stddev: " << dist.gaussian.stddev;
|
||||
break;
|
||||
case cutlass::Distribution::Identity:
|
||||
out << "identity";
|
||||
break;
|
||||
case cutlass::Distribution::Sequential:
|
||||
out << "sequential";
|
||||
break;
|
||||
default:
|
||||
out << "unknown";
|
||||
}
|
||||
|
||||
out << ", int_scale: " << dist.int_scale;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,62 @@
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are not permitted.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ exception semantics for CUDA error codes
|
||||
*/
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <iosfwd>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "cutlass/platform/platform.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
/// C++ exception wrapper for CUDA \p cudaError_t
|
||||
class cuda_exception : public std::exception {
|
||||
public:
|
||||
/// Constructor
|
||||
cuda_exception(const char* msg = "", cudaError_t err = cudaErrorUnknown) : msg(msg), err(err) {}
|
||||
|
||||
/// Returns the underlying CUDA \p cudaError_t
|
||||
cudaError_t cudaError() const { return err; }
|
||||
|
||||
protected:
|
||||
/// Explanatory string
|
||||
const char* msg;
|
||||
|
||||
/// Underlying CUDA \p cudaError_t
|
||||
cudaError_t err;
|
||||
};
|
||||
|
||||
/// Writes a cudaError_t to an output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, cudaError_t result) {
|
||||
return out << cudaGetErrorString(result);
|
||||
}
|
||||
|
||||
/// Writes a cuda_exception instance to an output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, cuda_exception const& e) {
|
||||
return out << e.what() << ": " << e.cudaError();
|
||||
}
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,63 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \file
|
||||
\brief reorder data from the host side
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/util/host_tensor.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/util/tensor_view_io.h"
|
||||
#include "cutlass/util/reference/host/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
template <int Interleaved, typename Element, typename Layout>
|
||||
void reorder_column(TensorRef<Element, Layout> dest,
|
||||
TensorRef<Element, Layout> src,
|
||||
cutlass::gemm::GemmCoord problem_size) {
|
||||
const int InstructionShapeCol = 8;
|
||||
// 4 threads per Quad
|
||||
const int ElementsPerThread = InstructionShapeCol / 4;
|
||||
// 4 threads per Quad
|
||||
const int ReorderedElementsPerThread =
|
||||
Interleaved / 4;
|
||||
|
||||
for (int n = 0; n < problem_size.n(); n++) {
|
||||
for (int k = 0; k < problem_size.k(); k++) {
|
||||
dest.at({k, (n / Interleaved) * Interleaved +
|
||||
((n % ReorderedElementsPerThread) / ElementsPerThread) *
|
||||
InstructionShapeCol +
|
||||
((n % Interleaved) / ReorderedElementsPerThread) *
|
||||
ElementsPerThread +
|
||||
(n % ElementsPerThread)}) = src.at({k, n});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,502 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
/*! \file
|
||||
\brief HostTensor contributes management for both host and device memory.
|
||||
|
||||
HostTensor allocates host and device memory upon construction. Basic element-wise operations on
|
||||
host memory synchronize device memory automatically. Explicit copy operations provide abstractions
|
||||
for CUDA memcpy operations.
|
||||
|
||||
Call {host, device}_{data, ref, view}() for accessing host or device memory.
|
||||
|
||||
See cutlass/tensor_ref.h and cutlass/tensor_view.h for more details.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_ref.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
|
||||
#include "device_memory.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Host tensor
|
||||
template <
|
||||
/// Data type of element stored within tensor (concept: NumericType)
|
||||
typename Element_,
|
||||
/// Defines a mapping from logical coordinate to linear memory (concept: Layout)
|
||||
typename Layout_
|
||||
>
|
||||
class HostTensor {
|
||||
public:
|
||||
|
||||
/// Data type of individual access
|
||||
using Element = Element_;
|
||||
|
||||
/// Mapping function from logical coordinate to linear memory
|
||||
using Layout = Layout_;
|
||||
|
||||
/// Logical rank of tensor index space
|
||||
static int const kRank = Layout::kRank;
|
||||
|
||||
/// Index type
|
||||
using Index = typename Layout::Index;
|
||||
|
||||
/// Long index used for pointer offsets
|
||||
using LongIndex = typename Layout::LongIndex;
|
||||
|
||||
/// Coordinate in logical tensor space
|
||||
using TensorCoord = typename Layout::TensorCoord;
|
||||
|
||||
/// Layout's stride vector
|
||||
using Stride = typename Layout::Stride;
|
||||
|
||||
/// Tensor reference to device memory
|
||||
using TensorRef = TensorRef<Element, Layout>;
|
||||
|
||||
/// Tensor reference to constant device memory
|
||||
using ConstTensorRef = typename TensorRef::ConstTensorRef;
|
||||
|
||||
/// Tensor reference to device memory
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
/// Tensor reference to constant device memory
|
||||
using ConstTensorView = typename TensorView::ConstTensorView;
|
||||
|
||||
/// Reference to element in tensor
|
||||
using Reference = typename TensorRef::Reference;
|
||||
|
||||
/// Constant reference to element in tensor
|
||||
using ConstReference = typename ConstTensorRef::Reference;
|
||||
|
||||
/// Used to handle packing of subbyte elements
|
||||
static int const kElementsPerStoredItem = (sizeof_bits<Element>::value < 8 ? sizeof(Element) * 8 / sizeof_bits<Element>::value : 1);
|
||||
|
||||
private:
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Extent of tensor in logical dimensions
|
||||
TensorCoord extent_;
|
||||
|
||||
/// Layout object
|
||||
Layout layout_;
|
||||
|
||||
/// Host-side memory allocation
|
||||
std::vector<Element> host_;
|
||||
|
||||
/// Device-side memory
|
||||
device_memory::allocation<Element> device_;
|
||||
|
||||
public:
|
||||
//
|
||||
// Device and Host Methods
|
||||
//
|
||||
|
||||
/// Default constructor
|
||||
HostTensor() {}
|
||||
|
||||
/// Constructs a tensor given an extent. Assumes a packed layout
|
||||
HostTensor(
|
||||
TensorCoord const &extent,
|
||||
bool device_backed = true
|
||||
) {
|
||||
|
||||
this->reset(extent, Layout::packed(extent), device_backed);
|
||||
}
|
||||
|
||||
/// Constructs a tensor given an extent and layout
|
||||
HostTensor(
|
||||
TensorCoord const &extent,
|
||||
Layout const &layout,
|
||||
bool device_backed = true
|
||||
) {
|
||||
|
||||
this->reset(extent, layout, device_backed);
|
||||
}
|
||||
|
||||
~HostTensor() { }
|
||||
|
||||
/// Clears the HostTensor allocation to size/capacity = 0
|
||||
void reset() {
|
||||
extent_ = TensorCoord();
|
||||
layout_ = Layout::packed(extent_);
|
||||
|
||||
host_.clear();
|
||||
device_.reset();
|
||||
}
|
||||
|
||||
/// Resizes internal memory allocations without affecting layout or extent
|
||||
void reserve(
|
||||
size_t count, ///< size of tensor in elements
|
||||
bool device_backed_ = true) { ///< if true, device memory is also allocated
|
||||
|
||||
device_.reset();
|
||||
host_.clear();
|
||||
|
||||
count /= kElementsPerStoredItem;
|
||||
|
||||
host_.resize(count);
|
||||
|
||||
// Allocate memory
|
||||
Element* device_memory = nullptr;
|
||||
if (device_backed_) {
|
||||
device_memory = device_memory::allocate<Element>(count);
|
||||
}
|
||||
device_.reset(device_memory, device_backed_ ? count : 0);
|
||||
}
|
||||
|
||||
/// Updates the extent and layout of the HostTensor. Allocates memory according to the new
|
||||
/// extent and layout.
|
||||
void reset(
|
||||
TensorCoord const &extent, ///< extent of logical tensor
|
||||
Layout const &layout, ///< layout object of tensor
|
||||
bool device_backed_ = true) { ///< if true, device memory is also allocated.
|
||||
|
||||
extent_ = extent;
|
||||
layout_ = layout;
|
||||
|
||||
reserve(size_t(layout_.capacity(extent_)), device_backed_);
|
||||
}
|
||||
|
||||
/// Updates the extent and layout of the HostTensor. Allocates memory according to the new
|
||||
/// extent and layout. Assumes a packed tensor configuration.
|
||||
void reset(
|
||||
TensorCoord const &extent, ///< extent of logical tensor
|
||||
bool device_backed_ = true) { ///< if true, device memory is also allocated.
|
||||
|
||||
reset(extent, Layout::packed(extent), device_backed_);
|
||||
}
|
||||
|
||||
/// Changes the size of the logical tensor. Only allocates memory if new capacity exceeds reserved capacity.
|
||||
/// To force allocation, call reset().
|
||||
void resize(
|
||||
TensorCoord const &extent, ///< extent of logical tensor
|
||||
Layout const &layout, ///< layout object of tensor
|
||||
bool device_backed_ = true) { ///< if true, device memory is also allocated.
|
||||
|
||||
extent_ = extent;
|
||||
layout_ = layout;
|
||||
|
||||
LongIndex new_size = size_t(layout_.capacity(extent_));
|
||||
|
||||
if (static_cast<decltype(host_.size())>(new_size) > host_.size()) {
|
||||
reserve(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the size of the logical tensor. Only allocates memory if new capacity exceeds reserved capacity.
|
||||
/// To force allocation, call reset(). Note, this form of resize() assumes a packed tensor configuration.
|
||||
void resize(
|
||||
TensorCoord const &extent, ///< extent of logical tensor
|
||||
bool device_backed_ = true) { ///< if true, device memory is also allocated.
|
||||
|
||||
resize(extent, Layout::packed(extent), device_backed_);
|
||||
}
|
||||
|
||||
/// Returns the number of elements stored in the host tensor
|
||||
size_t size() const {
|
||||
return host_.size() * kElementsPerStoredItem;
|
||||
}
|
||||
|
||||
/// Returns the logical capacity based on extent and layout. May differ from size().
|
||||
LongIndex capacity() const {
|
||||
return layout_.capacity(extent_) * kElementsPerStoredItem;
|
||||
}
|
||||
|
||||
/// Gets pointer to host data
|
||||
Element * host_data() { return host_.data(); }
|
||||
|
||||
/// Gets pointer to host data with a pointer offset
|
||||
Element * host_data_ptr_offset(LongIndex ptr_element_offset) { return host_.data() + ptr_element_offset; }
|
||||
|
||||
/// Gets a reference to an element in host memory
|
||||
Reference host_data(LongIndex idx) {
|
||||
return ReferenceFactory<Element>::get(host_data(), idx);
|
||||
}
|
||||
|
||||
/// Gets pointer to host data
|
||||
Element const * host_data() const { return host_.data(); }
|
||||
|
||||
/// Gets a constant reference to an element in host memory
|
||||
ConstReference host_data(LongIndex idx) const {
|
||||
return ReferenceFactory<Element const>::get(host_data(), idx);
|
||||
}
|
||||
|
||||
/// Gets pointer to device data
|
||||
Element * device_data() { return device_.get(); }
|
||||
|
||||
/// Gets pointer to device data with a pointer offset
|
||||
Element * device_data_ptr_offset(LongIndex ptr_element_offset) { return device_.get() + ptr_element_offset; }
|
||||
|
||||
/// Gets pointer to device data
|
||||
Element const * device_data() const { return device_.get(); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef host_ref(LongIndex ptr_element_offset=0) { return TensorRef(host_data_ptr_offset(ptr_element_offset), layout_); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
ConstTensorRef host_ref(LongIndex ptr_element_offset=0) const { return ConstTensorRef(host_data_ptr_offset(ptr_element_offset), layout_); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef device_ref(LongIndex ptr_element_offset=0) {
|
||||
return TensorRef(device_data_ptr_offset(ptr_element_offset), layout_);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
ConstTensorRef device_ref(LongIndex ptr_element_offset=0) const {
|
||||
return TensorRef(device_data_ptr_offset(ptr_element_offset), layout_);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorView host_view(LongIndex ptr_element_offset=0) {
|
||||
return TensorView(host_data_ptr_offset(ptr_element_offset), layout_, extent_);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
ConstTensorView host_view(LongIndex ptr_element_offset=0) const {
|
||||
return ConstTensorView(host_data_ptr_offset(ptr_element_offset), layout_, extent_);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorView device_view(LongIndex ptr_element_offset=0) {
|
||||
return TensorView(device_data_ptr_offset(ptr_element_offset), layout_, extent_);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
ConstTensorView device_view(LongIndex ptr_element_offset=0) const {
|
||||
return ConstTensorView(device_data_ptr_offset(ptr_element_offset), layout_, extent_);
|
||||
}
|
||||
|
||||
/// Returns true if device memory is allocated
|
||||
bool device_backed() const {
|
||||
return (device_.get() == nullptr) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
/// Returns the layout object
|
||||
Layout & layout() {
|
||||
return layout_;
|
||||
}
|
||||
|
||||
/// Returns the layout object
|
||||
Layout layout() const {
|
||||
return layout_;
|
||||
}
|
||||
|
||||
/// Returns the layout object's stride vector
|
||||
Stride stride() const {
|
||||
return layout_.stride();
|
||||
}
|
||||
|
||||
/// Returns the layout object's stride vector
|
||||
Stride & stride() {
|
||||
return layout_.stride();
|
||||
}
|
||||
|
||||
/// Returns the layout object's stride in a given physical dimension
|
||||
Index stride(int dim) const {
|
||||
return layout_.stride().at(dim);
|
||||
}
|
||||
|
||||
/// Returns the layout object's stride in a given physical dimension
|
||||
Index & stride(int dim) {
|
||||
return layout_.stride().at(dim);
|
||||
}
|
||||
|
||||
/// Computes the offset of an index from the origin of the tensor
|
||||
LongIndex offset(TensorCoord const& coord) const {
|
||||
return layout_(coord);
|
||||
}
|
||||
|
||||
/// Returns a reference to the element at the logical Coord in host memory
|
||||
Reference at(TensorCoord const& coord) {
|
||||
return host_data(offset(coord));
|
||||
}
|
||||
|
||||
/// Returns a const reference to the element at the logical Coord in host memory
|
||||
ConstReference at(TensorCoord const& coord) const {
|
||||
return host_data(offset(coord));
|
||||
}
|
||||
|
||||
/// Returns the extent of the tensor
|
||||
TensorCoord extent() const {
|
||||
return extent_;
|
||||
}
|
||||
|
||||
/// Returns the extent of the tensor
|
||||
TensorCoord & extent() {
|
||||
return extent_;
|
||||
}
|
||||
|
||||
/// Copies data from device to host
|
||||
void sync_host() {
|
||||
if (device_backed()) {
|
||||
device_memory::copy_to_host(
|
||||
host_data(), device_data(), size());
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies data from host to device
|
||||
void sync_device() {
|
||||
if (device_backed()) {
|
||||
device_memory::copy_to_device(
|
||||
device_data(), host_data(), size());
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_in_device_to_host(
|
||||
Element const* ptr_device, ///< source device memory
|
||||
LongIndex count = -1) { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_to_host(
|
||||
host_data(), ptr_device, count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_in_device_to_device(
|
||||
Element const* ptr_device, ///< source device memory
|
||||
LongIndex count = -1) { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_device_to_device(
|
||||
device_data(), ptr_device, count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_in_host_to_device(
|
||||
Element const* ptr_host, ///< source host memory
|
||||
LongIndex count = -1) { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_to_device(
|
||||
device_data(), ptr_host, count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_in_host_to_host(
|
||||
Element const* ptr_host, ///< source host memory
|
||||
LongIndex count = -1) { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_host_to_host(
|
||||
host_data(), ptr_host, count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_out_device_to_host(
|
||||
Element * ptr_host, ///< source device memory
|
||||
LongIndex count = -1) const { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_to_host(
|
||||
ptr_host, device_data(), count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_out_device_to_device(
|
||||
Element * ptr_device, ///< source device memory
|
||||
LongIndex count = -1) const { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_device_to_device(
|
||||
ptr_device, device_data(), count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_out_host_to_device(
|
||||
Element * ptr_device, ///< source host memory
|
||||
LongIndex count = -1) const { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_to_device(
|
||||
ptr_device, host_data(), count);
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer into host memory.
|
||||
void copy_out_host_to_host(
|
||||
Element * ptr_host, ///< source host memory
|
||||
LongIndex count = -1) const { ///< number of elements to transfer; if negative, entire tensor is overwritten.
|
||||
|
||||
if (count < 0) {
|
||||
count = capacity();
|
||||
}
|
||||
else {
|
||||
count = __NV_STD_MIN(capacity(), count);
|
||||
}
|
||||
device_memory::copy_host_to_host(
|
||||
ptr_host, host_data(), count);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,129 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for GEMM in host-side code.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/array.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace detail {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Template function to compute an inner product.
|
||||
#pragma hd_warning_disable // Suppresses warnings when attempting to instantiate with a
|
||||
// host-only type
|
||||
template <typename Atype, typename Btype, typename Ctype>
|
||||
CUTLASS_HOST_DEVICE
|
||||
Ctype inner_product(Atype a, Btype b, Ctype c) {
|
||||
return Ctype(a) * Ctype(b) + c;
|
||||
}
|
||||
|
||||
/// Specialization for matrix multiplication with binary operands
|
||||
template <>
|
||||
CUTLASS_HOST_DEVICE
|
||||
int inner_product<Array<bin1_t, 32>, Array<bin1_t, 32>, int>(
|
||||
Array<bin1_t, 32> a,
|
||||
Array<bin1_t, 32> b,
|
||||
int c) {
|
||||
|
||||
int accum = 0;
|
||||
for (int bit = 0; bit < 32; bit++) {
|
||||
accum += a[bit] ^ b[bit];
|
||||
}
|
||||
return accum + c;
|
||||
}
|
||||
|
||||
/*
|
||||
/// Specialization for matrix multiplication with signed 4-bit integer operands
|
||||
template <>
|
||||
CUTLASS_HOST_DEVICE
|
||||
int inner_product<Array<int4b_t, 8>, Array<int4b_t, 8>, int>(
|
||||
Array<int4b_t, 8> a,
|
||||
Array<int4b_t, 8> b,
|
||||
int c) {
|
||||
|
||||
int accum = 0;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
accum += a[k] * b[k];
|
||||
}
|
||||
return accum + c;
|
||||
}
|
||||
|
||||
/// Specialization for matrix multiplication with unsigned 4-bit integer operands
|
||||
template <>
|
||||
CUTLASS_HOST_DEVICE
|
||||
int inner_product<Array<uint4b_t, 8>, Array<uint4b_t, 8>, int>(
|
||||
Array<uint4b_t, 8> a,
|
||||
Array<uint4b_t, 8> b,
|
||||
int c) {
|
||||
|
||||
int accum = 0;
|
||||
for (int k = 0; k < 8; k++) {
|
||||
accum += a[k] * b[k];
|
||||
}
|
||||
return accum + c;
|
||||
}
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename SrcType, typename DstType>
|
||||
struct Cast {
|
||||
// Default behavior: convert to the destination type
|
||||
#pragma hd_warning_disable // Suppresses warnings when attempting to instantiate complex<T> with a
|
||||
// host-only type
|
||||
CUTLASS_HOST_DEVICE
|
||||
static DstType apply(SrcType src) { return static_cast<DstType>(src); };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Cast<float, int8_t> {
|
||||
CUTLASS_HOST_DEVICE
|
||||
static int8_t apply(float src) {
|
||||
// Clamp to the range of signed 8-bit integers.
|
||||
return static_cast<int8_t>(fmaxf(-128.f, fminf(127.f, src)));
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Cast<float, uint8_t> {
|
||||
CUTLASS_HOST_DEVICE
|
||||
static uint8_t apply(float src) {
|
||||
// Clamp to the range of signed 8-bit integers.
|
||||
return static_cast<uint8_t>(fmaxf(0.f, fminf(255.f, src)));
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace detail
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for GEMM in device-side code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
#include "cutlass/util/reference/device/kernel/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
///
|
||||
/// Explicitly naming types needed by this template can be cumbersome, particularly for the
|
||||
/// accumulator type, so a function argument 'initial_accum' is exposed. Passing
|
||||
/// AccumulatorType(0) as the last function argument can be easier than naming all template
|
||||
/// arguments explicitly.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename InnerProductOp = multiply_add<AccumulatorType>,
|
||||
typename ConvertOp = NumericConverter<ElementC, ScalarType>
|
||||
>
|
||||
void compute_gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 &&
|
||||
LayoutB::kRank == 2 &&
|
||||
LayoutC::kRank == 2, "Tensors must be of rank 2");
|
||||
|
||||
// Blocking structure potentially improves performance of reference implementation
|
||||
// with a minor increase in complexity.
|
||||
//
|
||||
// Note, this reference implementation is NOT expected to approach peak performance.
|
||||
using OutputTile = MatrixShape<4, 4>;
|
||||
|
||||
dim3 block(16, 8);
|
||||
|
||||
dim3 grid(
|
||||
(problem_size.m() + block.x * OutputTile::kRow - 1) / (block.x * OutputTile::kRow),
|
||||
(problem_size.n() + block.y * OutputTile::kColumn - 1) / (block.y * OutputTile::kColumn)
|
||||
);
|
||||
|
||||
// Launch a GEMM kernel
|
||||
kernel::Gemm<
|
||||
TensorRef<ElementA, LayoutA>,
|
||||
TensorRef<ElementB, LayoutB>,
|
||||
TensorRef<ElementC, LayoutC>,
|
||||
ScalarType,
|
||||
AccumulatorType,
|
||||
OutputTile,
|
||||
InnerProductOp,
|
||||
ConvertOp
|
||||
><<< grid, block >>>(
|
||||
problem_size,
|
||||
alpha,
|
||||
tensor_a,
|
||||
tensor_b,
|
||||
beta,
|
||||
tensor_c,
|
||||
tensor_d,
|
||||
initial_accum
|
||||
);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
///
|
||||
/// This assumes the accumulator type is the same type as the scalars.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename InnerProductOp = multiply_add<AccumulatorType>,
|
||||
typename ConvertOp = NumericConverter<ElementC, ScalarType>
|
||||
>
|
||||
void compute_gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, InnerProductOp, ConvertOp>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_c,
|
||||
initial_accum);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename InnerProductOp = cutlass::arch::OpMultiplyAdd
|
||||
>
|
||||
struct Gemm;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Partial specialization for multiply-add
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename AccumulatorType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, arch::OpMultiplyAdd> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, multiply_add<AccumulatorType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, multiply_add<AccumulatorType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Partial specialization for multiply-add-saturate
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename AccumulatorType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC, ScalarType,
|
||||
AccumulatorType, arch::OpMultiplyAddSaturate> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, multiply_add<AccumulatorType>,
|
||||
NumericConverterClamp<ElementC, ScalarType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, multiply_add<AccumulatorType>,
|
||||
NumericConverterClamp<ElementC, ScalarType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Parital specialization for XOR-popc
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename AccumulatorType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC, ScalarType,
|
||||
AccumulatorType, arch::OpXorPopc> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, xor_add<AccumulatorType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, AccumulatorType, xor_add<AccumulatorType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Batched GEMM
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a batch of GEMMs over a set of matrices of common dimension.
|
||||
//
|
||||
// TensorRefCollection* is a type satisfying the TensorRefCollection concept.
|
||||
//
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename InnerProductOp,
|
||||
typename ConvertOp
|
||||
>
|
||||
void BatchedGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
int batch_count,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA const& tensor_a,
|
||||
TensorRefCollectionB const& tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC &tensor_c,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
static_assert(
|
||||
TensorRefCollectionA::kRank == 2 &&
|
||||
TensorRefCollectionB::kRank == 2 &&
|
||||
TensorRefCollectionC::kRank == 2, "Tensors must be of rank 2");
|
||||
|
||||
// Blocking structure potentially improves performance of reference implementation
|
||||
// with a minor increase in complexity.
|
||||
//
|
||||
// Note, this reference implementation is NOT expected to approach peak performance.
|
||||
using OutputTile = MatrixShape<4, 4>;
|
||||
|
||||
dim3 block(16, 8);
|
||||
dim3 grid(
|
||||
(problem_size.m() + block.x * OutputTile::kRow - 1) / (block.x * OutputTile::kRow),
|
||||
(problem_size.n() + block.y * OutputTile::kColumn - 1) / (block.y * OutputTile::kColumn),
|
||||
batch_count
|
||||
);
|
||||
|
||||
// Launch a GEMM kernel
|
||||
kernel::BatchedGemm<
|
||||
TensorRefCollectionA,
|
||||
TensorRefCollectionB,
|
||||
TensorRefCollectionC,
|
||||
ScalarType,
|
||||
AccumulatorType,
|
||||
OutputTile,
|
||||
InnerProductOp,
|
||||
ConvertOp
|
||||
><<< grid, block >>>(
|
||||
problem_size,
|
||||
alpha,
|
||||
tensor_a,
|
||||
tensor_b,
|
||||
beta,
|
||||
tensor_c,
|
||||
initial_accum
|
||||
);
|
||||
}
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
//
|
||||
// TensorRefCollection* is a type satisfying the TensorRefCollection concept.
|
||||
//
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType
|
||||
>
|
||||
void BatchedGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
int batch_count,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA const& tensor_a,
|
||||
TensorRefCollectionB const& tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC &tensor_c) {
|
||||
|
||||
BatchedGemm(problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, ScalarType(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,157 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for GEMM in host-side code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
#include "cutlass/util/reference/device/thread/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
namespace kernel {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
template <
|
||||
typename TensorRefA,
|
||||
typename TensorRefB,
|
||||
typename TensorRefC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename OutputTile,
|
||||
typename InnerProductOp,
|
||||
typename ConvertOp
|
||||
>
|
||||
__global__ void Gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRefA tensor_a,
|
||||
TensorRefB tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefC tensor_c,
|
||||
TensorRefC tensor_d,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
// Map each thread to a unique tile of the output matrix
|
||||
MatrixCoord output_coord(
|
||||
(threadIdx.x + blockIdx.x * blockDim.x) * OutputTile::kRow,
|
||||
(threadIdx.y + blockIdx.y * blockDim.y) * OutputTile::kColumn
|
||||
);
|
||||
|
||||
// Compute the general matrix product
|
||||
thread::Gemm<
|
||||
TensorRefA,
|
||||
TensorRefB,
|
||||
TensorRefC,
|
||||
ScalarType,
|
||||
AccumulatorType,
|
||||
OutputTile,
|
||||
InnerProductOp,
|
||||
ConvertOp
|
||||
> gemm(initial_accum);
|
||||
|
||||
gemm.multiply_add(
|
||||
problem_size,
|
||||
tensor_a,
|
||||
tensor_b,
|
||||
output_coord);
|
||||
|
||||
gemm.epilogue(problem_size, alpha, beta, tensor_c, tensor_d, output_coord);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename OutputTile,
|
||||
typename InnerProductOp,
|
||||
typename ConvertOp
|
||||
>
|
||||
__global__ void BatchedGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA tensor_collection_a,
|
||||
TensorRefCollectionB tensor_collection_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC tensor_collection_c,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
// Obtain batch ID
|
||||
int batch_id = blockIdx.z;
|
||||
|
||||
// Dereference based on batch_id
|
||||
typename TensorRefCollectionA::TensorRef tensor_a = tensor_collection_a.at(batch_id);
|
||||
typename TensorRefCollectionB::TensorRef tensor_b = tensor_collection_b.at(batch_id);
|
||||
typename TensorRefCollectionC::TensorRef tensor_c = tensor_collection_c.at(batch_id);
|
||||
|
||||
// Map each thread to a unique tile of the output matrix
|
||||
MatrixCoord output_coord(
|
||||
(threadIdx.x + blockIdx.x * blockDim.x) * OutputTile::kColumn,
|
||||
(threadIdx.y + blockIdx.y * blockDim.y) * OutputTile::kRow
|
||||
);
|
||||
|
||||
// Compute the general matrix product
|
||||
thread::Gemm<
|
||||
typename TensorRefCollectionA::TensorRef,
|
||||
typename TensorRefCollectionB::TensorRef,
|
||||
typename TensorRefCollectionC::TensorRef,
|
||||
ScalarType,
|
||||
AccumulatorType,
|
||||
OutputTile,
|
||||
InnerProductOp,
|
||||
ConvertOp
|
||||
> gemm(initial_accum);
|
||||
|
||||
gemm.multiply_add(
|
||||
problem_size,
|
||||
tensor_a,
|
||||
tensor_b,
|
||||
output_coord);
|
||||
|
||||
gemm.epilogue(problem_size, alpha, beta, tensor_c, output_coord);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,162 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <curand_kernel.h>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
namespace kernel {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Kernel to initialize tensor to uniform random distribution
|
||||
template <typename T>
|
||||
__global__ void TensorInitializeUniform(
|
||||
Distribution dist, int64_t seed, int dim_contiguous, int dim_strided, T *tensor, int ldm) {
|
||||
__shared__ curandState_t rng_state[1024];
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * blockDim.x;
|
||||
|
||||
curand_init(seed, gtid, 0, &rng_state[threadIdx.x]);
|
||||
|
||||
int c_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int s_idx = blockIdx.y * blockDim.x;
|
||||
|
||||
tensor += s_idx * ldm + c_idx;
|
||||
|
||||
for (int s_offset = 0; s_offset < blockDim.x; ++s_offset, ++s_idx) {
|
||||
if (s_idx < dim_strided && c_idx < dim_contiguous) {
|
||||
double range = dist.uniform.max - dist.uniform.min;
|
||||
|
||||
double rnd = curand_uniform(&rng_state[threadIdx.x]);
|
||||
|
||||
rnd = dist.uniform.min + range * rnd;
|
||||
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
if (dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << dist.int_scale)));
|
||||
*tensor = T(rnd / double(1 << dist.int_scale));
|
||||
} else {
|
||||
*tensor = T(rnd);
|
||||
}
|
||||
|
||||
tensor += ldm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Kernel to initialize tensor to uniform distribution
|
||||
template <typename T>
|
||||
__global__ void TensorInitializeGaussian(
|
||||
Distribution dist, int64_t seed, int dim_contiguous, int dim_strided, T *tensor, int ldm) {
|
||||
__shared__ curandState_t rng_state[1024];
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * blockDim.x;
|
||||
|
||||
curand_init(seed, gtid, 0, &rng_state[threadIdx.x]);
|
||||
|
||||
int c_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int s_idx = blockIdx.y * blockDim.x;
|
||||
|
||||
tensor += s_idx * ldm + c_idx;
|
||||
|
||||
for (int s_offset = 0; s_offset < blockDim.x; ++s_offset, ++s_idx) {
|
||||
if (s_idx < dim_strided && c_idx < dim_contiguous) {
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
|
||||
double rnd = curand_normal(&rng_state[threadIdx.x]);
|
||||
|
||||
rnd = dist.gaussian.mean + dist.gaussian.stddev * rnd;
|
||||
|
||||
if (dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << dist.int_scale)));
|
||||
*tensor = T(rnd / double(1 << dist.int_scale));
|
||||
} else {
|
||||
*tensor = T(rnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kernel to initialize tensor to an identity matrix
|
||||
template <typename T>
|
||||
__global__ void TensorInitializeLinear(
|
||||
Distribution dist, int64_t seed, int dim_contiguous, int dim_strided, T *tensor, int ldm) {
|
||||
__shared__ curandState_t rng_state[1024];
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * blockDim.x;
|
||||
|
||||
curand_init(seed, gtid, 0, &rng_state[threadIdx.x]);
|
||||
|
||||
int c_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int s_idx = blockIdx.y * blockDim.x;
|
||||
|
||||
tensor += s_idx * ldm + c_idx;
|
||||
|
||||
for (int s_offset = 0; s_offset < blockDim.x; ++s_offset, ++s_idx) {
|
||||
if (s_idx < dim_strided && c_idx < dim_contiguous) {
|
||||
*tensor =
|
||||
dist.linear.offset + dist.linear.delta_row * c_idx + dist.linear.delta_column * s_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kernel to initialize tensor to an identity matrix
|
||||
template <typename T>
|
||||
__global__ void TensorInitializeIdentity(
|
||||
Distribution dist, int64_t seed, int dim_contiguous, int dim_strided, T *tensor, int ldm) {
|
||||
__shared__ curandState_t rng_state[1024];
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x + blockIdx.y * gridDim.x * blockDim.x;
|
||||
|
||||
curand_init(seed, gtid, 0, &rng_state[threadIdx.x]);
|
||||
|
||||
int c_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int s_idx = blockIdx.y * blockDim.x;
|
||||
|
||||
tensor += s_idx * ldm + c_idx;
|
||||
|
||||
for (int s_offset = 0; s_offset < blockDim.x; ++s_offset, ++s_idx) {
|
||||
if (s_idx < dim_strided && c_idx < dim_contiguous) {
|
||||
*tensor = (c_idx == s_idx ? T(1) : T(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,151 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/coord.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
namespace kernel {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Defines several helpers
|
||||
namespace detail {
|
||||
|
||||
/// Helper to perform for-each operation
|
||||
template <typename Func, int Rank, int RankRemaining>
|
||||
struct TensorForEachHelper {
|
||||
|
||||
/// Constructor for general rank
|
||||
__inline__ __device__
|
||||
TensorForEachHelper(Func &func, Coord<Rank> const &size, Coord<Rank> &coord, int64_t index) {
|
||||
|
||||
int64_t product = 1;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = Rank - RankRemaining; i < Rank; ++i) {
|
||||
product *= size[i];
|
||||
}
|
||||
|
||||
coord[Rank - 1 - RankRemaining] = index / product;
|
||||
int64_t remaining = index % product;
|
||||
|
||||
TensorForEachHelper<Func, Rank, RankRemaining-1>(func, size, coord, remaining);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper to perform for-each operation
|
||||
template <typename Func, int Rank>
|
||||
struct TensorForEachHelper<Func, Rank, 0> {
|
||||
|
||||
/// Constructor for fastest chaning rank
|
||||
__inline__ __device__
|
||||
TensorForEachHelper(Func &func, Coord<Rank> const &size, Coord<Rank> &coord, int64_t index) {
|
||||
|
||||
coord[Rank - 1] = index;
|
||||
|
||||
if (coord < size) {
|
||||
func(coord);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Kernel calls a functor for each element in a tensor's index space
|
||||
template <typename Func, int Rank, typename Params>
|
||||
__global__ void TensorForEach(Coord<Rank> size, Params params = Params()) {
|
||||
|
||||
Func func(params);
|
||||
|
||||
int64_t index = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int64_t max_index = 1;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < Rank; ++i) {
|
||||
max_index *= size[i];
|
||||
}
|
||||
|
||||
CUTLASS_PRAGMA_NO_UNROLL
|
||||
while (index < max_index) {
|
||||
Coord<Rank> coord;
|
||||
|
||||
detail::TensorForEachHelper<Func, Rank, Rank - 1>(func, size, coord, index);
|
||||
index += blockDim.x * gridDim.x;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Kernel calls a functor for each element along a tensor's diagonal
|
||||
template <typename Func, int Rank, typename Params>
|
||||
__global__ void TensorDiagonalForEach(Coord<Rank> size, Params params, int start, int end) {
|
||||
|
||||
Func func(params);
|
||||
|
||||
int64_t index = threadIdx.x + blockIdx.x * blockDim.x + start;
|
||||
|
||||
if (index < end) {
|
||||
Coord<Rank> coord;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < Rank; ++i) {
|
||||
coord[i] = index;
|
||||
}
|
||||
|
||||
func(coord);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Element, typename Func>
|
||||
__global__ void BlockForEach(
|
||||
Element *ptr,
|
||||
size_t capacity,
|
||||
typename Func::Params params) {
|
||||
|
||||
Func func(params);
|
||||
|
||||
size_t index = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
for (; index < capacity; index += blockDim.x * gridDim.x) {
|
||||
ptr[index] = func();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines host-side elementwise operations on TensorView.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
// Standard Library includes
|
||||
#include <utility>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/relatively_equal.h"
|
||||
|
||||
#include "cutlass/util/distribution.h"
|
||||
|
||||
#include "tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace kernel {
|
||||
|
||||
template <typename Element>
|
||||
__global__ void BlockCompareEqual(
|
||||
int *equal,
|
||||
Element const *ptr_A,
|
||||
Element const *ptr_B,
|
||||
size_t capacity) {
|
||||
|
||||
size_t idx = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
|
||||
for (; idx < capacity; idx += gridDim.x * blockDim.x) {
|
||||
if (ptr_A[idx] != ptr_B[idx]) {
|
||||
*equal = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Element>
|
||||
__global__ void BlockCompareRelativelyEqual(
|
||||
int *equal,
|
||||
Element const *ptr_A,
|
||||
Element const *ptr_B,
|
||||
size_t capacity,
|
||||
Element epsilon,
|
||||
Element nonzero_floor) {
|
||||
|
||||
size_t idx = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
|
||||
for (; idx < capacity; idx += gridDim.x * blockDim.x) {
|
||||
|
||||
Element a = ptr_A[idx];
|
||||
Element b = ptr_B[idx];
|
||||
|
||||
if (!relatively_equal(a, b, epsilon, nonzero_floor)) {
|
||||
*equal = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Performs a bit-level equality check between two blocks
|
||||
template <typename Element>
|
||||
bool BlockCompareEqual(
|
||||
Element const *ptr_A,
|
||||
Element const *ptr_B,
|
||||
size_t capacity,
|
||||
int grid_size = 0,
|
||||
int block_size = 0) {
|
||||
|
||||
int equal_flag = 1;
|
||||
int *device_equal_flag = nullptr;
|
||||
|
||||
if (cudaMalloc((void **)&device_equal_flag, sizeof(int)) != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to allocate device flag.");
|
||||
}
|
||||
|
||||
if (cudaMemcpy(
|
||||
device_equal_flag,
|
||||
&equal_flag,
|
||||
sizeof(int),
|
||||
cudaMemcpyHostToDevice) != cudaSuccess) {
|
||||
|
||||
throw std::runtime_error("Failed to copy equality flag to device.");
|
||||
}
|
||||
|
||||
if (!grid_size || !block_size) {
|
||||
|
||||
// if grid_size or block_size are zero, query occupancy using the CUDA Occupancy API
|
||||
cudaError_t result = cudaOccupancyMaxPotentialBlockSize(
|
||||
&grid_size,
|
||||
&block_size,
|
||||
reinterpret_cast<void const *>(kernel::BlockCompareEqual<Element>));
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to query occupancy.");
|
||||
}
|
||||
|
||||
// Limit block size. This has the effect of increasing the number of items processed by a
|
||||
// single thread and reduces the impact of initialization overhead.
|
||||
block_size = (block_size < 128 ? block_size : 128);
|
||||
}
|
||||
|
||||
dim3 grid(grid_size, 1, 1);
|
||||
dim3 block(block_size, 1, 1);
|
||||
|
||||
kernel::BlockCompareEqual<Element><<< grid, block >>>(device_equal_flag, ptr_A, ptr_B, capacity);
|
||||
|
||||
if (cudaMemcpy(
|
||||
&equal_flag,
|
||||
device_equal_flag,
|
||||
sizeof(int),
|
||||
cudaMemcpyDeviceToHost) != cudaSuccess) {
|
||||
|
||||
cudaFree(device_equal_flag);
|
||||
|
||||
throw std::runtime_error("Failed to copy equality flag from device.");
|
||||
}
|
||||
|
||||
cudaFree(device_equal_flag);
|
||||
|
||||
return equal_flag;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Performs a bit-level equality check between two blocks
|
||||
template <typename Element>
|
||||
bool BlockCompareRelativelyEqual(
|
||||
Element const *ptr_A,
|
||||
Element const *ptr_B,
|
||||
size_t capacity,
|
||||
Element epsilon,
|
||||
Element nonzero_floor,
|
||||
int grid_size = 0,
|
||||
int block_size = 0) {
|
||||
|
||||
int equal_flag = 1;
|
||||
int *device_equal_flag = nullptr;
|
||||
|
||||
if (cudaMalloc((void **)&device_equal_flag, sizeof(int)) != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to allocate device flag.");
|
||||
}
|
||||
|
||||
if (cudaMemcpy(
|
||||
device_equal_flag,
|
||||
&equal_flag,
|
||||
sizeof(int),
|
||||
cudaMemcpyHostToDevice) != cudaSuccess) {
|
||||
|
||||
throw std::runtime_error("Failed to copy equality flag to device.");
|
||||
}
|
||||
|
||||
if (!grid_size || !block_size) {
|
||||
|
||||
// if grid_size or block_size are zero, query occupancy using the CUDA Occupancy API
|
||||
cudaError_t result = cudaOccupancyMaxPotentialBlockSize(
|
||||
&grid_size,
|
||||
&block_size,
|
||||
reinterpret_cast<void const *>(kernel::BlockCompareRelativelyEqual<Element>));
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to query occupancy.");
|
||||
}
|
||||
|
||||
// Limit block size. This has the effect of increasing the number of items processed by a
|
||||
// single thread and reduces the impact of initialization overhead.
|
||||
block_size = (block_size < 128 ? block_size : 128);
|
||||
}
|
||||
|
||||
dim3 grid(grid_size, 1, 1);
|
||||
dim3 block(block_size, 1, 1);
|
||||
|
||||
kernel::BlockCompareRelativelyEqual<Element><<< grid, block >>>(
|
||||
device_equal_flag,
|
||||
ptr_A,
|
||||
ptr_B,
|
||||
capacity,
|
||||
epsilon,
|
||||
nonzero_floor
|
||||
);
|
||||
|
||||
if (cudaMemcpy(
|
||||
&equal_flag,
|
||||
device_equal_flag,
|
||||
sizeof(int),
|
||||
cudaMemcpyDeviceToHost) != cudaSuccess) {
|
||||
|
||||
cudaFree(device_equal_flag);
|
||||
|
||||
throw std::runtime_error("Failed to copy equality flag from device.");
|
||||
}
|
||||
|
||||
cudaFree(device_equal_flag);
|
||||
|
||||
return equal_flag;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // device
|
||||
} // reference
|
||||
} // cutlass
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/util/reference/device/kernel/tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Launches a kernel calling a functor for each element in a tensor's index space.
|
||||
template <typename Func, int Rank, typename Params>
|
||||
struct TensorForEach {
|
||||
|
||||
/// Constructor performs the operation.
|
||||
TensorForEach(Coord<Rank> size, Params params = Params(), int grid_size = 0, int block_size = 0) {
|
||||
|
||||
if (!grid_size || !block_size) {
|
||||
|
||||
// if grid_size or block_size are zero, query occupancy using the CUDA Occupancy API
|
||||
cudaError_t result = cudaOccupancyMaxPotentialBlockSize(
|
||||
&grid_size,
|
||||
&block_size,
|
||||
reinterpret_cast<void const *>(kernel::TensorForEach<Func, Rank, Params>));
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to query occupancy.");
|
||||
}
|
||||
|
||||
// Limit block size. This has the effect of increasing the number of items processed by a
|
||||
// single thread and reduces the impact of initialization overhead.
|
||||
block_size = (block_size < 128 ? block_size : 128);
|
||||
}
|
||||
|
||||
dim3 grid(grid_size, 1, 1);
|
||||
dim3 block(block_size, 1, 1);
|
||||
|
||||
kernel::TensorForEach<Func, Rank, Params><<< grid, block >>>(size, params);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Launches a kernel calling a functor for each element along a tensor's diagonal
|
||||
template <typename Func, int Rank, typename Params>
|
||||
struct TensorDiagonalForEach {
|
||||
|
||||
/// Constructor performs the operation
|
||||
TensorDiagonalForEach(Coord<Rank> size, Params params = Params(), int start = 0, int end = -1, int block_size = 128) {
|
||||
|
||||
if (end < 0) {
|
||||
end = size.min();
|
||||
}
|
||||
|
||||
dim3 block(block_size, 1, 1);
|
||||
dim3 grid((end - start + block_size - 1) / block_size, 1, 1);
|
||||
|
||||
kernel::TensorDiagonalForEach<Func, Rank, Params><<< grid, block >>>(size, params, start, end);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Element, typename Func>
|
||||
struct BlockForEach {
|
||||
|
||||
/// Constructor performs the operation.
|
||||
BlockForEach(
|
||||
Element *ptr,
|
||||
size_t capacity,
|
||||
typename Func::Params params = typename Func::Params(),
|
||||
int grid_size = 0,
|
||||
int block_size = 0) {
|
||||
|
||||
if (!grid_size || !block_size) {
|
||||
|
||||
// if grid_size or block_size are zero, query occupancy using the CUDA Occupancy API
|
||||
cudaError_t result = cudaOccupancyMaxPotentialBlockSize(
|
||||
&grid_size,
|
||||
&block_size,
|
||||
reinterpret_cast<void const *>(kernel::BlockForEach<Element, Func>));
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to query occupancy.");
|
||||
}
|
||||
|
||||
// Limit block size. This has the effect of increasing the number of items processed by a
|
||||
// single thread and reduces the impact of initialization overhead.
|
||||
block_size = (block_size < 128 ? block_size : 128);
|
||||
}
|
||||
|
||||
dim3 grid(grid_size, 1, 1);
|
||||
dim3 block(block_size, 1, 1);
|
||||
|
||||
kernel::BlockForEach<Element, Func><<< grid, block >>>(ptr, capacity, params);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namesace cutlass
|
||||
@@ -0,0 +1,181 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for GEMM in host-side code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
namespace thread {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Thread-level blocked general matrix product.
|
||||
//
|
||||
// Note, this is a reference implementation. Performance is not expected to approach peak.
|
||||
//
|
||||
template <
|
||||
typename TensorRefA,
|
||||
typename TensorRefB,
|
||||
typename TensorRefC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType,
|
||||
typename OutputTile,
|
||||
typename InnerProductOp = multiply_add<AccumulatorType>,
|
||||
typename ConvertOp = NumericConverter<typename TensorRefC::Element, ScalarType>
|
||||
>
|
||||
struct Gemm {
|
||||
|
||||
using ElementA = typename TensorRefA::Element;
|
||||
using ElementB = typename TensorRefB::Element;
|
||||
using ElementC = typename TensorRefC::Element;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Tile for A operand
|
||||
ElementA A_tile[OutputTile::kColumn];
|
||||
|
||||
/// Tile for B operand
|
||||
ElementB B_tile[OutputTile::kRow];
|
||||
|
||||
/// Tile for Accumulator
|
||||
AccumulatorType accum[OutputTile::kColumn][OutputTile::kRow];
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Gemm(AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
|
||||
// Clear fetch registers
|
||||
for (int i = 0; i < OutputTile::kColumn; ++i) {
|
||||
A_tile[i] = ElementA(0);
|
||||
}
|
||||
|
||||
for (int j = 0; j < OutputTile::kColumn; ++j) {
|
||||
B_tile[j] = ElementB(0);
|
||||
}
|
||||
|
||||
// Clear accumulators
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int j = 0; j < OutputTile::kColumn; ++j) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < OutputTile::kRow; ++i) {
|
||||
accum[j][i] = initial_accum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes a matrix product
|
||||
CUTLASS_HOST_DEVICE
|
||||
Gemm & multiply_add(
|
||||
gemm::GemmCoord problem_size,
|
||||
TensorRefA tensor_a,
|
||||
TensorRefB tensor_b,
|
||||
MatrixCoord output_coord = MatrixCoord()) {
|
||||
|
||||
InnerProductOp inner_product_op;
|
||||
|
||||
// Loop over the GEMM K dimension
|
||||
CUTLASS_PRAGMA_NO_UNROLL
|
||||
for (int k = 0; k < problem_size.k(); ++k) {
|
||||
|
||||
// Fetch a slice of the A matrix
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < OutputTile::kColumn; ++i) {
|
||||
if (output_coord.row() + i < problem_size.m()) {
|
||||
A_tile[i] = tensor_a.at(make_Coord(output_coord.row() + i, k));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch a slice of the B matrix
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int j = 0; j < OutputTile::kRow; ++j) {
|
||||
if (output_coord.column() + j < problem_size.n()) {
|
||||
B_tile[j] = tensor_b.at(make_Coord(k, output_coord.column() + j));
|
||||
}
|
||||
}
|
||||
|
||||
// Compute an accumulated matrix product
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int j = 0; j < OutputTile::kRow; ++j) {
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < OutputTile::kColumn; ++i) {
|
||||
accum[j][i] = inner_product_op(A_tile[i], B_tile[j], accum[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Performs linear scaling of matrix product and updates output tensor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Gemm & epilogue(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
ScalarType beta,
|
||||
TensorRefC tensor_c,
|
||||
TensorRefC tensor_d,
|
||||
MatrixCoord output_coord = MatrixCoord()) {
|
||||
|
||||
ConvertOp convert_op;
|
||||
|
||||
// Update the output tensor
|
||||
for (int j = 0; j < OutputTile::kRow; ++j) {
|
||||
for (int i = 0; i < OutputTile::kColumn; ++i) {
|
||||
MatrixCoord coord = output_coord + MatrixCoord(i, j);
|
||||
if (coord.row() < problem_size.m() && coord.column() < problem_size.n()) {
|
||||
|
||||
tensor_d.at(coord) = convert_op(
|
||||
alpha * ScalarType(accum[j][i]) +
|
||||
beta * ScalarType(tensor_c.at(coord))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace thread
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,376 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for GEMM in host-side code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
#include "cutlass/arch/mma.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename ComputeType,
|
||||
typename InnerProductOp = multiply_add<ComputeType>,
|
||||
typename ConvertOp = NumericConverter<ElementC, ScalarType>
|
||||
>
|
||||
void compute_gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
ComputeType initial_accum) {
|
||||
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 &&
|
||||
LayoutB::kRank == 2 &&
|
||||
LayoutC::kRank == 2, "Tensors must be of rank 2");
|
||||
|
||||
|
||||
// Note: batch is ignored.
|
||||
int const M = problem_size.m();
|
||||
int const N = problem_size.n();
|
||||
int const K = problem_size.k();
|
||||
|
||||
// Blocking necessary to speedup reference implementation
|
||||
int const Mblock = 16;
|
||||
int const Nblock = 16;
|
||||
|
||||
ConvertOp convert_op;
|
||||
InnerProductOp inner_product_op;
|
||||
|
||||
for (int row_block = 0; row_block < M; row_block += Mblock) {
|
||||
for (int col_block = 0; col_block < N; col_block += Nblock) {
|
||||
|
||||
ComputeType accum[Mblock][Nblock];
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
accum[i][j] = initial_accum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k_block = 0; k_block < K; ++k_block) {
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
int row = row_block + i;
|
||||
int col = col_block + j;
|
||||
|
||||
if (row < M && col < N) {
|
||||
ElementA a = tensor_a.at(MatrixCoord(row, k_block));
|
||||
ElementB b = tensor_b.at(MatrixCoord(k_block, col));
|
||||
|
||||
accum[i][j] = inner_product_op(ComputeType(a), ComputeType(b), accum[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
int row = row_block + i;
|
||||
int col = col_block + j;
|
||||
|
||||
MatrixCoord coord = MatrixCoord(row, col);
|
||||
|
||||
if (row < M && col < N) {
|
||||
tensor_d.at(coord) = convert_op(
|
||||
alpha * ScalarType(accum[i][j]) +
|
||||
beta * ScalarType(tensor_c.at(coord)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename ComputeType,
|
||||
typename InnerProductOp = multiply_add<ComputeType>,
|
||||
typename ConvertOp = NumericConverter<ElementC, ScalarType>
|
||||
>
|
||||
void compute_gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
ComputeType initial_accum) {
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, InnerProductOp, ConvertOp>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_c,
|
||||
initial_accum);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename ComputeType,
|
||||
typename InnerProductOp = cutlass::arch::OpMultiplyAdd
|
||||
>
|
||||
struct Gemm;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Partial specialization for multiply-add
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename ComputeType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC, ScalarType,
|
||||
ComputeType, arch::OpMultiplyAdd> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, multiply_add<ComputeType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, multiply_add<ComputeType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Partial specialization for multiply-add-saturate
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename ComputeType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC, ScalarType,
|
||||
ComputeType, arch::OpMultiplyAddSaturate> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, multiply_add<ComputeType>,
|
||||
NumericConverterClamp<ElementC, ScalarType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, multiply_add<ComputeType>,
|
||||
NumericConverterClamp<ElementC, ScalarType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Parital specialization for XOR-popc
|
||||
template <typename ElementA, typename LayoutA, typename ElementB,
|
||||
typename LayoutB, typename ElementC, typename LayoutC,
|
||||
typename ScalarType, typename ComputeType>
|
||||
struct Gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC, ScalarType,
|
||||
ComputeType, arch::OpXorPopc> {
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, xor_add<ComputeType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, initial_accum);
|
||||
}
|
||||
|
||||
void operator()(gemm::GemmCoord problem_size, ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b, ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
TensorRef<ElementC, LayoutC> tensor_d,
|
||||
ComputeType initial_accum = ComputeType(0)) {
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 && LayoutB::kRank == 2 && LayoutC::kRank == 2,
|
||||
"Tensors must be of rank 2");
|
||||
|
||||
compute_gemm<ElementA, LayoutA, ElementB, LayoutB, ElementC, LayoutC,
|
||||
ScalarType, ComputeType, xor_add<ComputeType>>(
|
||||
problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, tensor_d, initial_accum);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Batched GEMM
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a batch of GEMMs over a set of matrices of common dimension.
|
||||
//
|
||||
// TensorRefCollection* is a type satisfying the TensorRefCollection concept.
|
||||
//
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType
|
||||
>
|
||||
void BatchedGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
int batch_count,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA const& tensor_a,
|
||||
TensorRefCollectionB const& tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC &tensor_c,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
typename TensorRefCollectionA::ConstIterator tensor_a_it = tensor_a.begin();
|
||||
typename TensorRefCollectionB::ConstIterator tensor_b_it = tensor_b.begin();
|
||||
typename TensorRefCollectionC::ConstIterator tensor_c_it = tensor_c.begin();
|
||||
|
||||
for (int batch = 0;
|
||||
batch < batch_count;
|
||||
++batch, ++tensor_a_it, ++tensor_b_it, ++tensor_c_it) {
|
||||
|
||||
Gemm<typename TensorRefCollectionA::Element,
|
||||
typename TensorRefCollectionA::Layout,
|
||||
typename TensorRefCollectionB::Element,
|
||||
typename TensorRefCollectionB::Layout,
|
||||
typename TensorRefCollectionC::Element,
|
||||
typename TensorRefCollectionC::Layout,
|
||||
typename TensorRefCollectionC::Element,
|
||||
typename TensorRefCollectionC::Element>
|
||||
gemm;
|
||||
|
||||
gemm(problem_size, alpha, *tensor_a_it, *tensor_b_it, beta, *tensor_c_it,
|
||||
initial_accum);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
//
|
||||
// TensorRefCollection* is a type satisfying the TensorRefCollection concept.
|
||||
//
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType
|
||||
>
|
||||
void BatchedGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
int batch_count,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA const& tensor_a,
|
||||
TensorRefCollectionB const& tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC &tensor_c) {
|
||||
|
||||
BatchedGemm(problem_size, batch_count, alpha, tensor_a, tensor_b, beta, tensor_c, ScalarType(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,183 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Reference implementation for complex-valued GEMM in host-side code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/complex.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/functional.h"
|
||||
#include "cutlass/numeric_conversion.h"
|
||||
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "cutlass/gemm/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
///
|
||||
/// Explicitly naming types needed by this template can be cumbersome, particularly for the
|
||||
/// accumulator type, so a function argument 'initial_accum' is exposed. Passing
|
||||
/// AccumulatorType(0) as the last function argument can be easier than naming all template
|
||||
/// arguments explicitly.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType,
|
||||
typename ComputeType,
|
||||
typename ConvertOp = NumericConverter<ElementC, ScalarType>,
|
||||
typename InnerProductOp = multiply_add<ComputeType>
|
||||
>
|
||||
void GemmComplex(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
ComplexTransform transform_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ComplexTransform transform_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c,
|
||||
ComputeType initial_accum) {
|
||||
|
||||
static_assert(
|
||||
LayoutA::kRank == 2 &&
|
||||
LayoutB::kRank == 2 &&
|
||||
LayoutC::kRank == 2, "Tensors must be of rank 2");
|
||||
|
||||
// Note: batch is ignored.
|
||||
int const M = problem_size.m();
|
||||
int const N = problem_size.n();
|
||||
int const K = problem_size.k();
|
||||
|
||||
// Blocking necessary to speedup reference implementation
|
||||
int const Mblock = 16;
|
||||
int const Nblock = 16;
|
||||
|
||||
ConvertOp convert_op;
|
||||
InnerProductOp inner_product_op;
|
||||
|
||||
for (int row_block = 0; row_block < M; row_block += Mblock) {
|
||||
for (int col_block = 0; col_block < N; col_block += Nblock) {
|
||||
|
||||
ComputeType accum[Mblock][Nblock];
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
accum[i][j] = initial_accum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k_block = 0; k_block < K; ++k_block) {
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
int row = row_block + i;
|
||||
int col = col_block + j;
|
||||
|
||||
if (row < M && col < N) {
|
||||
ElementA a = tensor_a.at(MatrixCoord(row, k_block));
|
||||
ElementB b = tensor_b.at(MatrixCoord(k_block, col));
|
||||
|
||||
ComputeType a_ik = ComputeType(a);
|
||||
ComputeType b_kj = ComputeType(b);
|
||||
|
||||
if (transform_a == ComplexTransform::kConjugate) {
|
||||
a_ik = conj(a_ik);
|
||||
}
|
||||
|
||||
if (transform_b == ComplexTransform::kConjugate) {
|
||||
b_kj = conj(b_kj);
|
||||
}
|
||||
|
||||
accum[i][j] = inner_product_op(a_ik, b_kj, accum[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
int row = row_block + i;
|
||||
int col = col_block + j;
|
||||
|
||||
MatrixCoord coord = MatrixCoord(row, col);
|
||||
|
||||
if (row < M && col < N) {
|
||||
|
||||
tensor_c.at(coord) = convert_op(
|
||||
alpha * ScalarType(accum[i][j]) +
|
||||
beta * ScalarType(tensor_c.at(coord)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a general matrix product among matrices (tensors of rank=2) pointed to by TensorRef
|
||||
/// objects.
|
||||
///
|
||||
/// This assumes the accumulator type is the same type as the scalars.
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementC,
|
||||
typename LayoutC,
|
||||
typename ScalarType
|
||||
>
|
||||
void GemmComplex(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRef<ElementA, LayoutA> tensor_a,
|
||||
ComplexTransform transform_a,
|
||||
TensorRef<ElementB, LayoutB> tensor_b,
|
||||
ComplexTransform transform_b,
|
||||
ScalarType beta,
|
||||
TensorRef<ElementC, LayoutC> tensor_c) {
|
||||
|
||||
GemmComplex(problem_size, alpha, tensor_a, transform_a, tensor_b, transform_b, beta, tensor_c, ScalarType(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,245 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines host-side elementwise operations on TensorView.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard Library includes
|
||||
#include <utility>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/util/distribution.h"
|
||||
//#include "cutlass/util/type_traits.h"
|
||||
#include "tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorEqualsFunc {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView<Element, Layout> lhs;
|
||||
TensorView<Element, Layout> rhs;
|
||||
bool result;
|
||||
|
||||
/// Ctor
|
||||
TensorEqualsFunc(): result(true) { }
|
||||
|
||||
/// Ctor
|
||||
TensorEqualsFunc(
|
||||
TensorView<Element, Layout> const &lhs_,
|
||||
TensorView<Element, Layout> const &rhs_
|
||||
) :
|
||||
lhs(lhs_), rhs(rhs_), result(true) { }
|
||||
|
||||
/// Visits a coordinate
|
||||
void operator()(Coord<Layout::kRank> const &coord) {
|
||||
|
||||
Element lhs_ = lhs.at(coord);
|
||||
Element rhs_ = rhs.at(coord);
|
||||
|
||||
if (lhs_ != rhs_) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if equal
|
||||
operator bool() const {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if two tensor views are equal.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
bool TensorEquals(
|
||||
TensorView<Element, Layout> const &lhs,
|
||||
TensorView<Element, Layout> const &rhs) {
|
||||
|
||||
// Extents must be identical
|
||||
if (lhs.extent() != rhs.extent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::TensorEqualsFunc<Element, Layout> func(lhs, rhs);
|
||||
TensorForEach(
|
||||
lhs.extent(),
|
||||
func
|
||||
);
|
||||
|
||||
return bool(func);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if two tensor views are NOT equal.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
bool TensorNotEquals(
|
||||
TensorView<Element, Layout> const &lhs,
|
||||
TensorView<Element, Layout> const &rhs) {
|
||||
|
||||
// Extents must be identical
|
||||
if (lhs.extent() != rhs.extent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
detail::TensorEqualsFunc<Element, Layout> func(lhs, rhs);
|
||||
TensorForEach(
|
||||
lhs.extent(),
|
||||
func
|
||||
);
|
||||
|
||||
return !bool(func);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorContainsFunc {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView<Element, Layout> view;
|
||||
Element value;
|
||||
bool contains;
|
||||
Coord<Layout::kRank> location;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Ctor
|
||||
TensorContainsFunc(): contains(false) { }
|
||||
|
||||
/// Ctor
|
||||
TensorContainsFunc(
|
||||
TensorView<Element, Layout> const &view_,
|
||||
Element value_
|
||||
) :
|
||||
view(view_), value(value_), contains(false) { }
|
||||
|
||||
/// Visits a coordinate
|
||||
void operator()(Coord<Layout::kRank> const &coord) {
|
||||
|
||||
if (view.at(coord) == value) {
|
||||
if (!contains) {
|
||||
location = coord;
|
||||
}
|
||||
contains = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if equal
|
||||
operator bool() const {
|
||||
return contains;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if a value is present in a tensor
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
bool TensorContains(
|
||||
TensorView<Element, Layout> const & view,
|
||||
Element value) {
|
||||
|
||||
detail::TensorContainsFunc<Element, Layout> func(
|
||||
view,
|
||||
value
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
view.extent(),
|
||||
func
|
||||
);
|
||||
|
||||
return bool(func);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns a pair containing a boolean of whether a value exists in a tensor and the location of
|
||||
/// of the first occurrence. If the value is not contained in the tensor, the second element of the
|
||||
/// pair is undefined.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
std::pair<bool, Coord<Layout::kRank> > TensorFind(
|
||||
TensorView<Element, Layout> const & view,
|
||||
Element value) {
|
||||
|
||||
detail::TensorContainsFunc<Element, Layout> func(
|
||||
view,
|
||||
value
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
view.extent(),
|
||||
func
|
||||
);
|
||||
|
||||
return std::make_pair(bool(func), func.location);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,250 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines host-side elementwise operations on TensorView.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard Library includes
|
||||
#include <utility>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Helper to convert between types
|
||||
template <
|
||||
typename DstElement,
|
||||
typename SrcElement
|
||||
>
|
||||
struct TrivialConvert {
|
||||
|
||||
TrivialConvert() { }
|
||||
|
||||
DstElement operator()(SrcElement src) const {
|
||||
return DstElement(src);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper to conditionally copy between tensor views.
|
||||
template <
|
||||
typename DstElement,
|
||||
typename DstLayout,
|
||||
typename SrcElement,
|
||||
typename SrcLayout,
|
||||
typename F
|
||||
>
|
||||
struct TensorCopyIf {
|
||||
|
||||
using DstTensorView = TensorView<DstElement, DstLayout>;
|
||||
using SrcTensorView = TensorView<SrcElement, SrcLayout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
DstTensorView dst;
|
||||
SrcTensorView src;
|
||||
F convert;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorCopyIf() { }
|
||||
|
||||
TensorCopyIf(
|
||||
DstTensorView const &dst_,
|
||||
SrcTensorView const &src_,
|
||||
F const &convert_): dst(dst_), src(src_), convert(convert_) {}
|
||||
|
||||
/// Copies based on destination and source bounds
|
||||
void operator()(Coord<DstLayout::kRank> const &coord) {
|
||||
if (dst.contains(coord) && src.contains(coord)) {
|
||||
dst.at(coord) = convert(src.at(coord));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies elements from one tensor view into another, satisfying bounds of each tensor.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout, /// Source tensor's layout
|
||||
typename F /// Transformation functor
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorView<DstElement, DstLayout> dst,
|
||||
TensorView<SrcElement, SrcLayout> src,
|
||||
F const &transform) {
|
||||
|
||||
using CopyIf = detail::TensorCopyIf<
|
||||
DstElement,
|
||||
DstLayout,
|
||||
SrcElement,
|
||||
SrcLayout,
|
||||
F>;
|
||||
|
||||
CopyIf copy_if(dst, src, transform);
|
||||
|
||||
TensorForEach(dst.extent(), copy_if);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies elements from a TensorRef into a TensorView. Assumes source tensor has sufficient extent
|
||||
/// to avoid out of bounds accesses.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout, /// Source tensor's layout
|
||||
typename F /// Transformation functor
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorView<DstElement, DstLayout> dst,
|
||||
TensorRef<SrcElement, SrcLayout> src,
|
||||
F const &transform) {
|
||||
|
||||
using CopyIf = detail::TensorCopyIf<
|
||||
DstElement,
|
||||
DstLayout,
|
||||
SrcElement,
|
||||
SrcLayout,
|
||||
F>;
|
||||
|
||||
TensorView<SrcElement, SrcLayout> src_view(src, dst.extent());
|
||||
|
||||
CopyIf copy_if(dst, src_view, transform);
|
||||
|
||||
TensorForEach(dst.extent(), copy_if);
|
||||
}
|
||||
|
||||
/// Copies elements from a TensorRef into a TensorView. Assumes source tensor has sufficient extent
|
||||
/// to avoid out of bounds accesses.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout, /// Source tensor's layout
|
||||
typename F /// Transformation functor
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorRef<DstElement, DstLayout> dst,
|
||||
TensorView<SrcElement, SrcLayout> src,
|
||||
F const &transform) {
|
||||
|
||||
using CopyIf = detail::TensorCopyIf<
|
||||
DstElement,
|
||||
DstLayout,
|
||||
SrcElement,
|
||||
SrcLayout,
|
||||
F>;
|
||||
|
||||
TensorView<DstElement, DstLayout> dst_view(dst, src.extent());
|
||||
|
||||
CopyIf copy_if(dst_view, src, transform);
|
||||
|
||||
TensorForEach(src.extent(), copy_if);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies elements from one tensor view into another, satisfying bounds of each tensor. Succeeds
|
||||
/// if SrcElement can be converted to DstElement.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout /// Source tensor's layout
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorView<DstElement, DstLayout> dst,
|
||||
TensorView<SrcElement, SrcLayout> src) {
|
||||
|
||||
detail::TrivialConvert<DstElement, SrcElement> convert;
|
||||
|
||||
TensorCopy(dst, src, convert);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies elements from one tensor view into another, satisfying bounds of each tensor. Succeeds
|
||||
/// if SrcElement can be converted to DstElement.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout, /// Source tensor's layout
|
||||
typename F /// Transformation functor
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorView<DstElement, DstLayout> dst,
|
||||
TensorRef<SrcElement, SrcLayout> src) {
|
||||
|
||||
detail::TrivialConvert<DstElement, SrcElement> convert;
|
||||
|
||||
TensorCopy(dst, src, convert);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies elements from one tensor view into another, satisfying bounds of each tensor. Succeeds
|
||||
/// if SrcElement can be converted to DstElement.
|
||||
template <
|
||||
typename DstElement, /// Destination tensor's element type
|
||||
typename DstLayout, /// Destination tensor's layout
|
||||
typename SrcElement, /// Source tensor's element type
|
||||
typename SrcLayout /// Source tensor's layout
|
||||
>
|
||||
void TensorCopy(
|
||||
TensorRef<DstElement, DstLayout> dst,
|
||||
TensorView<SrcElement, SrcLayout> src) {
|
||||
|
||||
detail::TrivialConvert<DstElement, SrcElement> convert;
|
||||
|
||||
TensorCopy(dst, src, convert);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,335 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines host-side elementwise operations on TensorView.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/functional.h"
|
||||
|
||||
#include "tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to apply a binary operator in place
|
||||
template <
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB,
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename BinaryFunc>
|
||||
struct TensorFuncBinaryOp {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
TensorView<ElementD, LayoutD> view_d;
|
||||
TensorRef<ElementA, LayoutA> ref_a;
|
||||
TensorRef<ElementB, LayoutB> ref_b;
|
||||
BinaryFunc func;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
TensorFuncBinaryOp() { }
|
||||
|
||||
/// Constructor
|
||||
TensorFuncBinaryOp(
|
||||
TensorView<ElementD, LayoutD> const & view_d_,
|
||||
TensorRef<ElementA, LayoutA> const & ref_a_,
|
||||
TensorRef<ElementB, LayoutB> const & ref_b_,
|
||||
BinaryFunc func = BinaryFunc()
|
||||
):
|
||||
view_d(view_d_), view_a(view_a_), view_b(view_b_), func(func) { }
|
||||
|
||||
/// Equality check
|
||||
void operator()(Coord<LayoutD::kRank> const &coord) const {
|
||||
view_d.at(coord) = func(
|
||||
ElementD(view_a.at(coord)),
|
||||
ElementD(view_b.at(coord))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Adds two tensors and stores in the destination tensor: d = a + b
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorAdd(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a, ///< A tensor reference
|
||||
TensorRef<ElementB, LayoutB> b ///< B tensor reference
|
||||
) {
|
||||
|
||||
detail::TensorFuncBinaryOp<
|
||||
ElementD,
|
||||
LayoutD,
|
||||
ElementA,
|
||||
LayoutA,
|
||||
ElementB,
|
||||
LayoutB,
|
||||
cutlass::plus<ElementD>
|
||||
> func(d, a, b);
|
||||
|
||||
TensorForEach(
|
||||
d.extent(),
|
||||
func);
|
||||
}
|
||||
|
||||
/// Adds a tensor in place: d = d .+ a
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA
|
||||
>
|
||||
void TensorAdd(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a ///< A tensor reference
|
||||
) {
|
||||
TensorAdd(d, d, a);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Subtracts two tensors and stores in the destination tensor: d = a - b
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorSub(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a, ///< A tensor reference
|
||||
TensorRef<ElementB, LayoutB> b ///< B tensor reference
|
||||
) {
|
||||
|
||||
detail::TensorFuncBinaryOp<
|
||||
ElementD,
|
||||
LayoutD,
|
||||
ElementA,
|
||||
LayoutA,
|
||||
ElementB,
|
||||
LayoutB,
|
||||
cutlass::minus<ElementD>
|
||||
> func(d, a, b);
|
||||
|
||||
TensorForEach(
|
||||
d.extent(),
|
||||
func);
|
||||
}
|
||||
|
||||
/// Subtracts two tensors in place: d = d .- a
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorSub(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a ///< A tensor reference
|
||||
) {
|
||||
|
||||
TensorSub(d, d, a);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Multiplies two tensors and stores in the destination tensor: d = a .* b
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorMul(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a, ///< A tensor reference
|
||||
TensorRef<ElementB, LayoutB> b ///< B tensor reference
|
||||
) {
|
||||
|
||||
detail::TensorFuncBinaryOp<
|
||||
ElementD,
|
||||
LayoutD,
|
||||
ElementA,
|
||||
LayoutA,
|
||||
ElementB,
|
||||
LayoutB,
|
||||
cutlass::multiplies<ElementD>
|
||||
> func(d, a, b);
|
||||
|
||||
TensorForEach(
|
||||
d.extent(),
|
||||
func);
|
||||
}
|
||||
|
||||
/// Multiplies tensors in place: d = d .* a
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA
|
||||
>
|
||||
void TensorMul(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a ///< A tensor reference
|
||||
) {
|
||||
TensorMul(d, d, a);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Divides two tensors and stores in the destination tensor: d = a ./ b
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorDiv(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a, ///< A tensor reference
|
||||
TensorRef<ElementB, LayoutB> b ///< B tensor reference
|
||||
) {
|
||||
|
||||
detail::TensorFuncBinaryOp<
|
||||
ElementD,
|
||||
LayoutD,
|
||||
ElementA,
|
||||
LayoutA,
|
||||
ElementB,
|
||||
LayoutB,
|
||||
cutlass::divides<ElementD>
|
||||
> func(d, a, b);
|
||||
|
||||
TensorForEach(
|
||||
d.extent(),
|
||||
func);
|
||||
}
|
||||
|
||||
/// Divides tensors in place: d = d ./ a
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA
|
||||
>
|
||||
void TensorDiv(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a ///< A tensor reference
|
||||
) {
|
||||
TensorMul(d, d, a);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Divides two tensors and stores in the destination tensor: d = a ./ b
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA,
|
||||
typename ElementB,
|
||||
typename LayoutB
|
||||
>
|
||||
void TensorModulus(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a, ///< A tensor reference
|
||||
TensorRef<ElementB, LayoutB> b ///< B tensor reference
|
||||
) {
|
||||
|
||||
detail::TensorFuncBinaryOp<
|
||||
ElementD,
|
||||
LayoutD,
|
||||
ElementA,
|
||||
LayoutA,
|
||||
ElementB,
|
||||
LayoutB,
|
||||
cutlass::modulus<ElementD>
|
||||
> func(d, a, b);
|
||||
|
||||
TensorForEach(
|
||||
d.extent(),
|
||||
func);
|
||||
}
|
||||
|
||||
/// Divides tensors in place: d = d ./ a
|
||||
template <
|
||||
typename ElementD,
|
||||
typename LayoutD,
|
||||
typename ElementA,
|
||||
typename LayoutA
|
||||
>
|
||||
void TensorModulus(
|
||||
TensorView<ElementD, LayoutD> d, ///< destination tensor view
|
||||
TensorRef<ElementA, LayoutA> a ///< A tensor reference
|
||||
) {
|
||||
TensorMul(d, d, a);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,853 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Provides several functions for filling tensors with data.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard Library includes
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/complex.h"
|
||||
#include "cutlass/array.h"
|
||||
#include "cutlass/numeric_types.h"
|
||||
|
||||
#include "cutlass/util/distribution.h"
|
||||
#include "tensor_foreach.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorFillFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
Element value;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorFillFunc(
|
||||
TensorView const &view_ = TensorView(),
|
||||
Element value_ = Element(0)
|
||||
): view(view_), value(value_) { }
|
||||
|
||||
void operator()(Coord<Layout::kRank> const & coord) const {
|
||||
view.at(coord) = value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor with a uniform value
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFill(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element val = Element(0)) { ///< value to uniformly fill it with
|
||||
|
||||
detail::TensorFillFunc<Element, Layout> func(dst, val);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Element>
|
||||
struct RandomGaussianFunc {
|
||||
|
||||
uint64_t seed;
|
||||
double mean;
|
||||
double stddev;
|
||||
int int_scale;
|
||||
double pi;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
RandomGaussianFunc(
|
||||
uint64_t seed_ = 0,
|
||||
double mean_ = 0,
|
||||
double stddev_ = 1,
|
||||
int int_scale_ = -1
|
||||
):
|
||||
seed(seed_), mean(mean_), stddev(stddev_), int_scale(int_scale_), pi(std::acos(-1)) {
|
||||
std::srand((unsigned)seed);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
Element operator()() const {
|
||||
|
||||
// Box-Muller transform to generate random numbers with Normal distribution
|
||||
double u1 = double(std::rand()) / double(RAND_MAX);
|
||||
double u2 = double(std::rand()) / double(RAND_MAX);
|
||||
|
||||
// Compute Gaussian random value
|
||||
double rnd = std::sqrt(-2 * std::log(u1)) * std::cos(2 * pi * u2);
|
||||
rnd = mean + stddev * rnd;
|
||||
|
||||
// Scale and convert final result
|
||||
Element result;
|
||||
|
||||
if (int_scale >= 0) {
|
||||
rnd = double(int64_t(rnd * double(1 << int_scale))) / double(1 << int_scale);
|
||||
result = static_cast<Element>(rnd);
|
||||
}
|
||||
else {
|
||||
result = static_cast<Element>(rnd);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Partial specialization for initializing a complex value.
|
||||
template <typename Element>
|
||||
struct RandomGaussianFunc<complex<Element> > {
|
||||
|
||||
uint64_t seed;
|
||||
double mean;
|
||||
double stddev;
|
||||
int int_scale;
|
||||
double pi;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
RandomGaussianFunc(
|
||||
uint64_t seed_ = 0,
|
||||
double mean_ = 0,
|
||||
double stddev_ = 1,
|
||||
int int_scale_ = -1
|
||||
):
|
||||
seed(seed_), mean(mean_), stddev(stddev_), int_scale(int_scale_), pi(std::acos(-1)) {
|
||||
std::srand((unsigned)seed);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
complex<Element> operator()() const {
|
||||
|
||||
Element reals[2];
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// Box-Muller transform to generate random numbers with Normal distribution
|
||||
double u1 = double(std::rand()) / double(RAND_MAX);
|
||||
double u2 = double(std::rand()) / double(RAND_MAX);
|
||||
|
||||
// Compute Gaussian random value
|
||||
double rnd = std::sqrt(-2 * std::log(u1)) * std::cos(2 * pi * u2);
|
||||
rnd = mean + stddev * rnd;
|
||||
|
||||
if (int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << int_scale)));
|
||||
reals[i] = from_real<Element>(rnd / double(1 << int_scale));
|
||||
}
|
||||
else {
|
||||
reals[i] = from_real<Element>(rnd);
|
||||
}
|
||||
}
|
||||
|
||||
return complex<Element>(reals[0], reals[1]);
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a random Gaussian distribution
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorFillGaussianFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
RandomGaussianFunc<Element> func;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Construction of Gaussian RNG functor.
|
||||
TensorFillGaussianFunc(
|
||||
TensorView view_ = TensorView(),
|
||||
RandomGaussianFunc<Element> func_ = RandomGaussianFunc<Element>()
|
||||
):
|
||||
view(view_), func(func_) {
|
||||
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
void operator()(Coord<Layout::kRank> const &coord) const {
|
||||
view.at(coord) = func();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor with random values with a Gaussian distribution.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillRandomGaussian(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
uint64_t seed, ///< seed for RNG
|
||||
double mean = 0, ///< Gaussian distribution's mean
|
||||
double stddev = 1, ///< Gaussian distribution's standard deviation
|
||||
int bits = -1) { ///< If non-negative, specifies number of fractional bits that
|
||||
/// are not truncated to zero. Permits reducing precision of
|
||||
/// data.
|
||||
|
||||
detail::RandomGaussianFunc<Element> random_func(seed, mean, stddev, bits);
|
||||
|
||||
detail::TensorFillGaussianFunc<Element, Layout> func(
|
||||
dst,
|
||||
random_func
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor with random values with a Gaussian distribution.
|
||||
template <
|
||||
typename Element ///< Element type
|
||||
>
|
||||
void BlockFillRandomGaussian(
|
||||
Element *ptr, ///< destination buffer
|
||||
size_t capacity, ///< number of elements
|
||||
uint64_t seed, ///< seed for RNG
|
||||
double mean = 0, ///< Gaussian distribution's mean
|
||||
double stddev = 1, ///< Gaussian distribution's standard deviation
|
||||
int bits = -1) { ///< If non-negative, specifies number of fractional bits that
|
||||
/// are not truncated to zero. Permits reducing precision of
|
||||
/// data.
|
||||
|
||||
|
||||
detail::RandomGaussianFunc<Element> random_func(seed, mean, stddev, bits);
|
||||
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
ptr[i] = random_func();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Element>
|
||||
struct RandomUniformFunc {
|
||||
|
||||
using Real = typename RealType<Element>::Type;
|
||||
|
||||
uint64_t seed;
|
||||
double range;
|
||||
double min;
|
||||
int int_scale;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
RandomUniformFunc(
|
||||
uint64_t seed_ = 0,
|
||||
double max = 1,
|
||||
double min_ = 0,
|
||||
int int_scale_ = -1
|
||||
):
|
||||
seed(seed_), range(max - min_), min(min_), int_scale(int_scale_) {
|
||||
std::srand((unsigned)seed);
|
||||
}
|
||||
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
Element operator()() const {
|
||||
|
||||
double rnd = double(std::rand()) / double(RAND_MAX);
|
||||
|
||||
rnd = min + range * rnd;
|
||||
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
Element result;
|
||||
|
||||
if (int_scale >= 0) {
|
||||
rnd = double(int64_t(rnd * double(1 << int_scale))) / double(1 << int_scale);
|
||||
result = static_cast<Element>(Real(rnd));
|
||||
}
|
||||
else {
|
||||
result = static_cast<Element>(Real(rnd));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Partial specialization for initializing a complex value.
|
||||
template <typename Element>
|
||||
struct RandomUniformFunc<complex<Element> > {
|
||||
|
||||
using Real = typename RealType<Element>::Type;
|
||||
|
||||
uint64_t seed;
|
||||
double range;
|
||||
double min;
|
||||
int int_scale;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
RandomUniformFunc(
|
||||
uint64_t seed_ = 0,
|
||||
double max = 1,
|
||||
double min_ = 0,
|
||||
int int_scale_ = -1
|
||||
):
|
||||
seed(seed_), range(max - min_), min(min_), int_scale(int_scale_) {
|
||||
std::srand((unsigned)seed);
|
||||
}
|
||||
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
complex<Element> operator()() const {
|
||||
|
||||
Element reals[2];
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
double rnd = double(std::rand()) / double(RAND_MAX);
|
||||
|
||||
rnd = min + range * rnd;
|
||||
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
|
||||
if (int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << int_scale)));
|
||||
reals[i] = from_real<Element>(Real(rnd / double(1 << int_scale)));
|
||||
}
|
||||
else {
|
||||
reals[i] = from_real<Element>(Real(rnd));
|
||||
}
|
||||
}
|
||||
|
||||
return complex<Element>(reals[0], reals[1]);
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a random Gaussian distribution
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorFillRandomUniformFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
RandomUniformFunc<Element> func;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Construction of Gaussian RNG functor.
|
||||
TensorFillRandomUniformFunc(
|
||||
TensorView view_ = TensorView(),
|
||||
RandomUniformFunc<Element> func_ = RandomUniformFunc<Element>()
|
||||
):
|
||||
view(view_), func(func_) {
|
||||
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
void operator()(Coord<Layout::kRank> const &coord) const {
|
||||
|
||||
view.at(coord) = func();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor with random values with a uniform random distribution.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillRandomUniform(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
uint64_t seed, ///< seed for RNG
|
||||
double max = 1, ///< upper bound of distribution
|
||||
double min = 0, ///< lower bound for distribution
|
||||
int bits = -1) { ///< If non-negative, specifies number of fractional bits that
|
||||
/// are not truncated to zero. Permits reducing precision of
|
||||
/// data.
|
||||
detail::RandomUniformFunc<Element> random_func(seed, max, min, bits);
|
||||
|
||||
detail::TensorFillRandomUniformFunc<Element, Layout> func(
|
||||
dst,
|
||||
random_func
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor with random values with a uniform random distribution.
|
||||
template <
|
||||
typename Element ///< Element type
|
||||
>
|
||||
void BlockFillRandomUniform(
|
||||
Element *ptr,
|
||||
size_t capacity,
|
||||
uint64_t seed, ///< seed for RNG
|
||||
double max = 1, ///< upper bound of distribution
|
||||
double min = 0, ///< lower bound for distribution
|
||||
int bits = -1) { ///< If non-negative, specifies number of fractional bits that
|
||||
/// are not truncated to zero. Permits reducing precision of
|
||||
/// data.
|
||||
detail::RandomUniformFunc<Element> random_func(seed, max, min, bits);
|
||||
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
ptr[i] = random_func();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorFillDiagonalFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
Element diag;
|
||||
Element other;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorFillDiagonalFunc(
|
||||
TensorView const &view_ = TensorView(),
|
||||
Element diag_ = Element(1),
|
||||
Element other_ = Element(0)
|
||||
):
|
||||
view(view_), diag(diag_), other(other_) { }
|
||||
|
||||
void operator()(Coord<Layout::kRank> const & coord) const {
|
||||
bool is_diag = true;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < Layout::kRank; ++i) {
|
||||
if (coord[i] != coord[i - 1]) {
|
||||
is_diag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
view.at(coord) = (is_diag ? diag : other);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a tensor everywhere with a unique value for its diagonal.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillDiagonal(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element diag = Element(1), ///< value to write in the diagonal
|
||||
Element other = Element(0)) { ///< value to write off the diagonal
|
||||
|
||||
detail::TensorFillDiagonalFunc<Element, Layout> func(
|
||||
dst,
|
||||
diag,
|
||||
other
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to fill a tensor's digonal with 1 and 0 everywhere else.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillIdentity(
|
||||
TensorView<Element, Layout> dst) { ///< destination tensor
|
||||
|
||||
TensorFillDiagonal(dst, Element(1), Element(0));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Writes a uniform value to the diagonal of a tensor without modifying off-diagonal elements.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorUpdateDiagonal(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element val = Element(1)) {
|
||||
|
||||
typename Layout::Index extent = dst.extent().min();
|
||||
|
||||
for (typename Layout::Index i = 0; i < extent; ++i) {
|
||||
Coord<Layout::kRank> coord(i);
|
||||
dst.at(coord) = val;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorUpdateOffDiagonalFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
Element other;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorUpdateOffDiagonalFunc(
|
||||
TensorView const &view_ = TensorView(),
|
||||
Element other_ = Element(0)
|
||||
):
|
||||
view(view_), other(other_) { }
|
||||
|
||||
void operator()(Coord<Layout::kRank> const & coord) const {
|
||||
bool is_diag = true;
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < Layout::kRank; ++i) {
|
||||
if (coord[i] != coord[i - 1]) {
|
||||
is_diag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_diag) {
|
||||
view.at(coord) = other;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Writes a uniform value to all elements in the tensor without modifying diagonal elements.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorUpdateOffDiagonal(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element other = Element(1)) {
|
||||
|
||||
detail::TensorUpdateOffDiagonalFunc<Element, Layout> func(
|
||||
dst,
|
||||
other
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
struct TensorFillLinearFunc {
|
||||
|
||||
using TensorView = TensorView<Element, Layout>;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
TensorView view;
|
||||
Array<Element, Layout::kRank> v;
|
||||
Element s;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorFillLinearFunc() { }
|
||||
|
||||
/// Constructs functor
|
||||
TensorFillLinearFunc(
|
||||
TensorView const &view_,
|
||||
Array<Element, Layout::kRank> const & v_,
|
||||
Element s_ = Element(0)
|
||||
):
|
||||
view(view_), v(v_), s(s_) { }
|
||||
|
||||
/// Updates the tensor
|
||||
void operator()(Coord<Layout::kRank> const & coord) const {
|
||||
|
||||
Element sum(s);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < Layout::kRank; ++i) {
|
||||
sum += Element(coord[i]) * v[i];
|
||||
}
|
||||
|
||||
view.at(coord) = sum;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills tensor with a linear combination of its coordinate and another vector
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillLinear(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Array<Element, Layout::kRank> const & v,
|
||||
Element s = Element(0)) {
|
||||
|
||||
detail::TensorFillLinearFunc<Element, Layout> func(
|
||||
dst,
|
||||
v,
|
||||
s
|
||||
);
|
||||
|
||||
TensorForEach(
|
||||
dst.extent(),
|
||||
func
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills tensor with a linear combination of its coordinate and another vector
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorFillSequential(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element s = Element(0)) {
|
||||
|
||||
Array<Element, Layout::kRank> stride;
|
||||
|
||||
stride[0] = Element(1);
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 1; i < Layout::kRank; ++i) {
|
||||
stride[i] = stride[i - 1] * Element(dst.extent()[i - 1]);
|
||||
}
|
||||
|
||||
TensorFillLinear(dst, stride, s);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a block of data with sequential elements
|
||||
template <
|
||||
typename Element
|
||||
>
|
||||
void BlockFillSequential(
|
||||
Element *ptr,
|
||||
int64_t capacity,
|
||||
Element v = Element(1),
|
||||
Element s = Element(0)) {
|
||||
int i = 0;
|
||||
|
||||
while (i < capacity) {
|
||||
cutlass::ReferenceFactory<Element, (cutlass::sizeof_bits<Element>::value <
|
||||
8)>::get(ptr, i) = s;
|
||||
|
||||
s = Element(s + v);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Fills a block of data with sequential elements
|
||||
template <
|
||||
typename Element
|
||||
>
|
||||
void BlockFillRandom(
|
||||
Element *ptr,
|
||||
size_t capacity,
|
||||
uint64_t seed,
|
||||
Distribution dist) {
|
||||
|
||||
if (dist.kind == Distribution::Gaussian) {
|
||||
BlockFillRandomGaussian<Element>(
|
||||
ptr,
|
||||
capacity,
|
||||
seed,
|
||||
dist.gaussian.mean,
|
||||
dist.gaussian.stddev,
|
||||
dist.int_scale);
|
||||
}
|
||||
else if (dist.kind == Distribution::Uniform) {
|
||||
BlockFillRandomUniform<Element>(
|
||||
ptr,
|
||||
capacity,
|
||||
seed,
|
||||
dist.uniform.max,
|
||||
dist.uniform.min,
|
||||
dist.int_scale);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies a diagonal in from host memory without modifying off-diagonal elements.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorCopyDiagonalIn(
|
||||
TensorView<Element, Layout> dst, ///< destination tensor
|
||||
Element const *ptr) { ///< dense buffer of elements
|
||||
|
||||
typename Layout::Index extent = dst.extent().min();
|
||||
|
||||
for (typename Layout::Index i = 0; i < extent; ++i) {
|
||||
Coord<Layout::kRank> coord(i);
|
||||
dst.at(coord) = ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Copies the diagonal of a tensor into a dense buffer in host memory.
|
||||
template <
|
||||
typename Element, ///< Element type
|
||||
typename Layout> ///< Layout function
|
||||
void TensorCopyDiagonalOut(
|
||||
Element *ptr, ///< dense buffer of elements
|
||||
TensorView<Element, Layout> src) { ///< source tensor
|
||||
|
||||
typename Layout::Index extent = src.extent().min();
|
||||
|
||||
for (typename Layout::Index i = 0; i < extent; ++i) {
|
||||
Coord<Layout::kRank> coord(i);
|
||||
ptr[i] = src.at(coord);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,128 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include "cutlass/cutlass.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Defines several helpers
|
||||
namespace detail {
|
||||
|
||||
/// Helper to perform for-each operation
|
||||
template <typename Func, int Rank, int RankRemaining>
|
||||
struct TensorForEachHelper {
|
||||
|
||||
/// Index of the active rank
|
||||
static int const kActiveRank = Rank - RankRemaining - 1;
|
||||
|
||||
/// Constructor for general rank
|
||||
TensorForEachHelper(
|
||||
Func &func,
|
||||
Coord<Rank> const &extent,
|
||||
Coord<Rank> &coord) {
|
||||
|
||||
for (int i = 0; i < extent.at(kActiveRank); ++i) {
|
||||
coord[kActiveRank] = i;
|
||||
TensorForEachHelper<Func, Rank, RankRemaining - 1>(func, extent, coord);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper to perform for-each operation
|
||||
template <typename Func, int Rank>
|
||||
struct TensorForEachHelper<Func, Rank, 0> {
|
||||
|
||||
/// Index of the active rank
|
||||
static int const kActiveRank = Rank - 1;
|
||||
|
||||
/// Constructor for fastest chaning rank
|
||||
TensorForEachHelper(
|
||||
Func &func,
|
||||
Coord<Rank> const &extent,
|
||||
Coord<Rank> &coord) {
|
||||
|
||||
for (int i = 0; i < extent.at(kActiveRank); ++i) {
|
||||
coord[kActiveRank] = i;
|
||||
func(coord);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Iterates over the index space of a tensor
|
||||
template <
|
||||
typename Func, ///< function applied to each point in a tensor's index space
|
||||
int Rank> ///< rank of index space
|
||||
void TensorForEach(Coord<Rank> extent, Func & func) {
|
||||
Coord<Rank> coord;
|
||||
detail::TensorForEachHelper<Func, Rank, Rank - 1>(func, extent, coord);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Iterates over the index space of a tensor and calls a C++ lambda
|
||||
template <
|
||||
typename Func, ///< function applied to each point in a tensor's index space
|
||||
int Rank> ///< rank of index space
|
||||
void TensorForEachLambda(Coord<Rank> extent, Func func) {
|
||||
Coord<Rank> coord;
|
||||
detail::TensorForEachHelper<Func, Rank, Rank - 1>(func, extent, coord);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Element, typename Func>
|
||||
struct BlockForEach {
|
||||
|
||||
/// Constructor performs the operation.
|
||||
BlockForEach(
|
||||
Element *ptr,
|
||||
size_t capacity,
|
||||
typename Func::Params params = typename Func::Params()) {
|
||||
|
||||
Func func(params);
|
||||
|
||||
for (size_t index = 0; index < capacity; ++index) {
|
||||
ptr[index] = func();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,76 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/complex.h"
|
||||
#include "cutlass/tensor_ref.h"
|
||||
|
||||
#include "cutlass/util/reference/host/tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes the p=2 norm of the elements of a tensor with arbitrary reduction data type.
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout,
|
||||
typename ElementReduction
|
||||
>
|
||||
ElementReduction TensorNorm(
|
||||
TensorView<Element, Layout> view,
|
||||
ElementReduction accumulator) {
|
||||
|
||||
TensorForEachLambda(
|
||||
view.extent(),
|
||||
[&](typename Layout::TensorCoord const & coord) {
|
||||
Element element = Element(view.at(coord));
|
||||
accumulator = cutlass::norm_accumulate(element, accumulator);
|
||||
});
|
||||
return std::sqrt(accumulator);
|
||||
}
|
||||
|
||||
/// Computes the p=2 norm of the elements of a tensor.
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout
|
||||
>
|
||||
double TensorNorm(TensorView<Element, Layout> view) {
|
||||
|
||||
return TensorNorm<Element, Layout, double>(view, 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,146 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "cutlass/core_io.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Helper to write the least significant rank of a TensorView
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout
|
||||
>
|
||||
inline std::ostream & TensorView_WriteLeastSignificantRank(
|
||||
std::ostream& out,
|
||||
TensorView<Element, Layout> const& view,
|
||||
Coord<Layout::kRank> const &start_coord,
|
||||
int rank,
|
||||
std::streamsize width) {
|
||||
|
||||
for (int idx = 0; idx < view.extent(rank); ++idx) {
|
||||
|
||||
Coord<Layout::kRank> coord(start_coord);
|
||||
coord[rank] = idx;
|
||||
|
||||
if (idx) {
|
||||
out.width(0);
|
||||
out << ", ";
|
||||
}
|
||||
if (idx || coord) {
|
||||
out.width(width);
|
||||
}
|
||||
out << ScalarIO<Element>(view.at(coord));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Helper to write a rank of a TensorView
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout
|
||||
>
|
||||
inline std::ostream & TensorView_WriteRank(
|
||||
std::ostream& out,
|
||||
TensorView<Element, Layout> const& view,
|
||||
Coord<Layout::kRank> const &start_coord,
|
||||
int rank,
|
||||
std::streamsize width) {
|
||||
|
||||
// If called on the least significant rank, write the result as a row
|
||||
if (rank + 1 == Layout::kRank) {
|
||||
return TensorView_WriteLeastSignificantRank(out, view, start_coord, rank, width);
|
||||
}
|
||||
|
||||
// Otherwise, write a sequence of rows and newlines
|
||||
for (int idx = 0; idx < view.extent(rank); ++idx) {
|
||||
|
||||
Coord<Layout::kRank> coord(start_coord);
|
||||
coord[rank] = idx;
|
||||
|
||||
if (rank + 2 == Layout::kRank) {
|
||||
// Write least significant ranks asa matrix with rows delimited by ";\n"
|
||||
out << (idx ? ";\n" : "");
|
||||
TensorView_WriteLeastSignificantRank(out, view, coord, rank + 1, width);
|
||||
}
|
||||
else {
|
||||
// Higher ranks are separated by newlines
|
||||
out << (idx ? "\n" : "");
|
||||
TensorView_WriteRank(out, view, coord, rank + 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Prints human-readable representation of a TensorView to an ostream
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout
|
||||
>
|
||||
inline std::ostream& TensorViewWrite(
|
||||
std::ostream& out,
|
||||
TensorView<Element, Layout> const& view) {
|
||||
|
||||
// Prints a TensorView according to the following conventions:
|
||||
// - least significant rank is printed as rows separated by ";\n"
|
||||
// - all greater ranks are delimited with newlines
|
||||
//
|
||||
// The result is effectively a whitespace-delimited series of 2D matrices.
|
||||
|
||||
return detail::TensorView_WriteRank(out, view, Coord<Layout::kRank>(), 0, out.width());
|
||||
}
|
||||
|
||||
/// Prints human-readable representation of a TensorView to an ostream
|
||||
template <
|
||||
typename Element,
|
||||
typename Layout
|
||||
>
|
||||
inline std::ostream& operator<<(
|
||||
std::ostream& out,
|
||||
TensorView<Element, Layout> const& view) {
|
||||
|
||||
// Prints a TensorView according to the following conventions:
|
||||
// - least significant rank is printed as rows separated by ";\n"
|
||||
// - all greater ranks are delimited with newlines
|
||||
//
|
||||
// The result is effectively a whitespace-delimited series of 2D matrices.
|
||||
|
||||
return TensorViewWrite(out, view);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,232 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Type traits for common CUDA types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_fp16.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/complex.h"
|
||||
|
||||
namespace cutlass {
|
||||
struct half_t;
|
||||
|
||||
template <typename T>
|
||||
struct TypeTraits {
|
||||
typedef T host_type;
|
||||
typedef T device_type;
|
||||
static inline T remove_negative_zero(T x) { return x; }
|
||||
static inline T to_print(T x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<int8_t> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_8I;
|
||||
typedef int8_t host_type;
|
||||
typedef int8_t device_type;
|
||||
typedef int8_t integer_type;
|
||||
typedef uint8_t unsigned_type;
|
||||
static inline int8_t remove_negative_zero(int8_t x) { return x; }
|
||||
static inline int to_print(int8_t x) { return (int)x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<uint8_t> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_8I;
|
||||
typedef uint8_t host_type;
|
||||
typedef uint8_t device_type;
|
||||
typedef uint8_t integer_type;
|
||||
typedef uint8_t unsigned_type;
|
||||
static inline uint8_t remove_negative_zero(uint8_t x) { return x; }
|
||||
static inline uint32_t to_print(uint8_t x) { return (uint32_t)x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<int> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32I;
|
||||
typedef int host_type;
|
||||
typedef int device_type;
|
||||
typedef int32_t integer_type;
|
||||
typedef uint32_t unsigned_type;
|
||||
static inline int32_t remove_negative_zero(int32_t x) { return x; }
|
||||
static inline int to_print(int x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<unsigned> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32I;
|
||||
typedef unsigned host_type;
|
||||
typedef unsigned device_type;
|
||||
typedef uint32_t integer_type;
|
||||
typedef uint32_t unsigned_type;
|
||||
static inline uint32_t remove_negative_zero(uint32_t x) { return x; }
|
||||
static inline uint32_t to_print(uint32_t x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<int64_t> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_8I;
|
||||
typedef int64_t host_type;
|
||||
typedef int64_t device_type;
|
||||
typedef int64_t integer_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
static inline int64_t remove_negative_zero(int64_t x) { return x; }
|
||||
static inline int64_t to_print(int64_t x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<uint64_t> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_8I;
|
||||
typedef uint64_t host_type;
|
||||
typedef uint64_t device_type;
|
||||
typedef uint64_t integer_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
static inline uint64_t remove_negative_zero(uint64_t x) { return x; }
|
||||
static inline uint64_t to_print(uint64_t x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<half_t> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_16F;
|
||||
typedef half_t host_type;
|
||||
typedef half_t device_type;
|
||||
typedef int16_t integer_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
static inline half_t remove_negative_zero(half_t x) {
|
||||
return (x.raw() == 0x8000 ? half_t::bitcast(0) : x);
|
||||
}
|
||||
static inline half_t to_print(half_t x) { return x; }
|
||||
static inline device_type to_device(half_t x) { return reinterpret_cast<device_type const &>(x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<float> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32F;
|
||||
typedef float host_type;
|
||||
typedef float device_type;
|
||||
typedef int32_t integer_type;
|
||||
typedef uint32_t unsigned_type;
|
||||
static inline float remove_negative_zero(float x) { return x == -0.f ? 0.f : x; }
|
||||
static inline float to_print(float x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<double> {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_64F;
|
||||
typedef double host_type;
|
||||
typedef double device_type;
|
||||
typedef int64_t integer_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
static inline double remove_negative_zero(double x) { return x == -0.0 ? 0.0 : x; }
|
||||
static inline double to_print(double x) { return x; }
|
||||
static inline device_type to_device(host_type x) { return x; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Complex types
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <>
|
||||
struct TypeTraits<complex<half> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_16F;
|
||||
typedef complex<half_t> host_type;
|
||||
typedef complex<half> device_type;
|
||||
typedef int16_t integer_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
static inline device_type to_device(complex<half> x) { return reinterpret_cast<device_type const &>(x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<complex<half_t> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_16F;
|
||||
typedef complex<half_t> host_type;
|
||||
typedef complex<half> device_type;
|
||||
typedef int16_t integer_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
static inline complex<half_t> remove_negative_zero(complex<half_t> x) {
|
||||
return complex<half_t>(
|
||||
real(x) == -0_hf ? 0_hf : real(x),
|
||||
imag(x) == -0_hf ? 0_hf : imag(x)
|
||||
);
|
||||
}
|
||||
static inline complex<half_t> to_print(complex<half_t> x) { return x; }
|
||||
static inline device_type to_device(complex<half_t> x) { return reinterpret_cast<device_type const &>(x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<complex<float> > {
|
||||
|
||||
static cudaDataType_t const cublas_type = CUDA_C_32F;
|
||||
typedef complex<float> host_type;
|
||||
typedef complex<float> device_type;
|
||||
typedef int64_t integer_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
|
||||
static inline complex<float> remove_negative_zero(complex<float> x) {
|
||||
return complex<float>(
|
||||
real(x) == -0.f ? 0.f : real(x),
|
||||
imag(x) == -0.f ? 0.f : imag(x)
|
||||
);
|
||||
}
|
||||
|
||||
static inline complex<float> to_print(complex<float> x) { return x; }
|
||||
static inline device_type to_device(complex<float> x) { return reinterpret_cast<device_type const &>(x); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<complex<double> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_64F;
|
||||
typedef complex<double> host_type;
|
||||
typedef complex<double> device_type;
|
||||
struct integer_type { int64_t real, imag; };
|
||||
struct unsigned_type { uint64_t real, imag; };
|
||||
static inline complex<double> remove_negative_zero(complex<double> x) {
|
||||
return complex<double>(
|
||||
real(x) == -0.0 ? 0.0 : real(x),
|
||||
imag(x) == -0.0 ? 0.0 : imag(x)
|
||||
);
|
||||
}
|
||||
static inline complex<double> to_print(complex<double> x) { return x; }
|
||||
static inline device_type to_device(complex<double> x) { return reinterpret_cast<device_type const &>(x); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
Reference in New Issue
Block a user