Checkpointing CUTLASS 1.1 release.
This commit is contained in:
+50
-23
@@ -108,7 +108,7 @@ struct CommandLine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the commandline parameter for a given index (not including flags)
|
||||
* Returns 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;
|
||||
@@ -156,27 +156,7 @@ struct CommandLine {
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
if (keys[i] == string(arg_name)) {
|
||||
string val_string(values[i]);
|
||||
istringstream str_stream(val_string);
|
||||
string::size_type old_pos = 0;
|
||||
string::size_type new_pos = 0;
|
||||
|
||||
// Iterate <sep>-delimited values
|
||||
value_t val;
|
||||
while ((new_pos = val_string.find(sep, old_pos)) != 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);
|
||||
seperate_string(val_string, vals, sep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +164,7 @@ struct CommandLine {
|
||||
|
||||
/**
|
||||
* Returns the values specified for a given commandline parameter
|
||||
* --<flag>=<key:value>,<key:value>*
|
||||
* --<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,
|
||||
@@ -198,6 +178,26 @@ struct CommandLine {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -249,6 +249,33 @@ struct CommandLine {
|
||||
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
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cutlass/util/debug.h>
|
||||
#include <cutlass/util/platform.h>
|
||||
#include <tools/util/exceptions.h>
|
||||
#include "cutlass/util/debug.h"
|
||||
#include "cutlass/util/platform.h"
|
||||
#include "tools/util/exceptions.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace device_memory {
|
||||
@@ -124,6 +124,10 @@ struct allocation {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Number of elements of T allocated on the current CUDA device
|
||||
size_t capacity;
|
||||
|
||||
@@ -131,7 +135,7 @@ struct allocation {
|
||||
platform::unique_ptr<T, deleter> smart_ptr;
|
||||
|
||||
//
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor: allocates no memory
|
||||
@@ -140,6 +144,11 @@ struct allocation {
|
||||
/// 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(); }
|
||||
|
||||
@@ -172,6 +181,16 @@ struct allocation {
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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, Linear, Identity };
|
||||
|
||||
/// 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 offset;
|
||||
double delta_row;
|
||||
double delta_column;
|
||||
} linear;
|
||||
};
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Configures distribution as linear combination of row and column index
|
||||
Distribution &set_linear(double _offset, double _delta_row, double _delta_column) {
|
||||
kind = Linear;
|
||||
linear.offset = _offset;
|
||||
linear.delta_row = _delta_row;
|
||||
linear.delta_column = _delta_column;
|
||||
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::Linear:
|
||||
out << "linear, mean: " << dist.linear.offset << ", delta_row: " << dist.linear.delta_row
|
||||
<< ", delta_column: " << dist.linear.delta_column;
|
||||
break;
|
||||
case cutlass::Distribution::Identity:
|
||||
break;
|
||||
default:
|
||||
out << "unknown";
|
||||
}
|
||||
|
||||
out << ", int_scale: " << dist.int_scale;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <iosfwd>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cutlass/util/platform.h>
|
||||
#include "cutlass/util/platform.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
|
||||
+27
-30
@@ -107,6 +107,33 @@ class half_t {
|
||||
uint16_t& raw() { return x; }
|
||||
uint16_t raw() const { return x; }
|
||||
|
||||
//
|
||||
// Stream interactions
|
||||
//
|
||||
|
||||
/// put to stream - half_t-precision types bitcast as unsigned shorts if base is hexadecimal
|
||||
friend std::ostream& operator<<(std::ostream& out, cutlass::half_t const& h) {
|
||||
if (out.flags() & std::ios::hex) {
|
||||
return out << h.x;
|
||||
} else {
|
||||
return out << float(h);
|
||||
}
|
||||
}
|
||||
|
||||
/// read from stream - half_t-precision types parsed as unsigned shorts if base is hexadecimal
|
||||
friend std::istream& operator>>(std::istream& in, cutlass::half_t& h) {
|
||||
if (in.flags() & std::ios::hex) {
|
||||
unsigned short u = 0;
|
||||
in >> u;
|
||||
h = cutlass::half_t::bitcast(u);
|
||||
} else {
|
||||
float f = 0;
|
||||
in >> f;
|
||||
h = cutlass::half_t(f);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
public:
|
||||
/// data
|
||||
unsigned short x;
|
||||
@@ -167,9 +194,6 @@ cutlass::half_t operator-(float, cutlass::half_t const&);
|
||||
cutlass::half_t operator*(float, cutlass::half_t const&);
|
||||
cutlass::half_t operator/(float, cutlass::half_t const&);
|
||||
|
||||
std::ostream& operator<<(std::ostream&, cutlass::half_t const&); /// writes a half_t
|
||||
std::istream& operator>>(std::istream&, cutlass::half_t&); /// reads a half_t
|
||||
|
||||
#ifdef BOOST_LEXICAL_CAST_INCLUDED
|
||||
namespace boost {
|
||||
|
||||
@@ -714,30 +738,3 @@ inline cutlass::half_t sqrt(cutlass::half_t const& h) {
|
||||
return cutlass::half_t(std::sqrt(float(h)));
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
//
|
||||
// Stream interactions
|
||||
//
|
||||
|
||||
/// put to stream - half_t-precision types bitcast as unsigned shorts if base is hexadecimal
|
||||
inline std::ostream& operator<<(std::ostream& out, cutlass::half_t const& h) {
|
||||
if (out.flags() & std::ios::hex) {
|
||||
return out << h.x;
|
||||
} else {
|
||||
return out << float(h);
|
||||
}
|
||||
}
|
||||
|
||||
/// read from stream - half_t-precision types parsed as unsigned shorts if base is hexadecimal
|
||||
inline std::istream& operator>>(std::istream& in, cutlass::half_t& h) {
|
||||
if (in.flags() & std::ios::hex) {
|
||||
unsigned short u = 0;
|
||||
in >> u;
|
||||
h = cutlass::half_t::bitcast(u);
|
||||
} else {
|
||||
float f = 0;
|
||||
in >> f;
|
||||
h = cutlass::half_t(f);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 HostMatrix is a helper to define a HostTensor of rank=2 with a contiguous layout.
|
||||
|
||||
See tools/util/host_tensor.h for more details.
|
||||
*/
|
||||
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "tools/util/host_tensor.h"
|
||||
|
||||
#include "tools/util/reference/host/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with contiguous layout
|
||||
template <
|
||||
typename T
|
||||
>
|
||||
class HostMatrix :
|
||||
public HostTensor<T, 2, MatrixLayout::ContiguousLayout, 3, int, long long> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensor of rank=2 with contiguous layout
|
||||
typedef HostTensor<T, 2, MatrixLayout::ContiguousLayout, 3, int, long long> Base;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
private:
|
||||
|
||||
/// Layout of contiguous matrix
|
||||
MatrixLayout::Kind layout_;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrix(): layout_(MatrixLayout::kColumnMajor) { }
|
||||
|
||||
/// Constructs a HostTensor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrix(MatrixCoord const& size, bool _device_backed = true): layout_(MatrixLayout::kColumnMajor) {
|
||||
Index ldm = size[0];
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Constructs a HostTensor from size and layout - infers leading dimension
|
||||
HostMatrix(MatrixCoord const& size, MatrixLayout::Kind layout, bool _device_backed = true): layout_(layout) {
|
||||
Index ldm = (layout_ == MatrixLayout::kColumnMajor ? size[0] : size[1]);
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Constructs a HostTensor given size, layout, and leading dimension
|
||||
HostMatrix(MatrixCoord const& size, Index ldm, MatrixLayout::Kind layout, bool _device_backed = true): layout_(layout) {
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return layout_;
|
||||
}
|
||||
|
||||
/// Resizes a matrix
|
||||
void resize(MatrixCoord const &_size, MatrixLayout::Kind layout, Index ldm = 0, bool _device_backed = true) {
|
||||
if (!ldm) {
|
||||
ldm = (layout == MatrixLayout::kColumnMajor ? _size[0] : _size[1]);
|
||||
}
|
||||
layout_ = layout;
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), _size, _device_backed);
|
||||
}
|
||||
|
||||
/// Helper to resize matrix
|
||||
void resize(Index rows, Index columns, MatrixLayout::Kind layout, Index ldm = 0, bool _device_backed = true) {
|
||||
this->resize(MatrixCoord(rows, columns), layout, ldm,_device_backed);
|
||||
}
|
||||
|
||||
/// Helper to resize matrix
|
||||
void resize_matrix(Index rows, Index columns, MatrixLayout::Kind layout, Index ldm = 0, bool _device_backed = true) {
|
||||
this->resize(MatrixCoord(rows, columns), layout, ldm,_device_backed);
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
if (layout_ == MatrixLayout::kColumnMajor) {
|
||||
return this->stride(MatrixLayout::ContiguousLayout::kColumn);
|
||||
}
|
||||
else {
|
||||
return this->stride(MatrixLayout::ContiguousLayout::kRow);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
|
||||
/// Returns size in the given dimension
|
||||
Index size(int idx) const {
|
||||
return Base::size(idx);
|
||||
}
|
||||
|
||||
/// Helper to call GEMM operation on HostMatrix objects that differ only in their scalar type.
|
||||
template <typename A, typename B, typename Ctype, typename Stype>
|
||||
void gemm(
|
||||
HostMatrix<A> const& tensor_a,
|
||||
HostMatrix<B> const& tensor_b,
|
||||
Stype alpha = Stype(1),
|
||||
Stype beta = Stype(0)) {
|
||||
|
||||
gemm::GemmCoord problem_size(
|
||||
tensor_a.size().column(),
|
||||
this->size().column(),
|
||||
this->size().row(),
|
||||
1);
|
||||
|
||||
cutlass::reference::host::Gemm(
|
||||
problem_size,
|
||||
alpha,
|
||||
tensor_a,
|
||||
tensor_b,
|
||||
beta,
|
||||
*this,
|
||||
Ctype(0));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with column-major layout
|
||||
template <
|
||||
typename T
|
||||
>
|
||||
class HostMatrixColumnMajor :
|
||||
public HostTensor<T, 2, MatrixLayout::ColumnMajor, 2, int, long long> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensor of rank=2 with contiguous layout
|
||||
typedef HostTensor<T, 2, MatrixLayout::ColumnMajor, 2, int, long long> Base;
|
||||
|
||||
/// Tensor coordinate
|
||||
typedef typename Base::TensorCoord TensorCoord;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrixColumnMajor() { }
|
||||
|
||||
/// Constructs a HostMatrixColumnMajor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrixColumnMajor(TensorCoord const& size, bool _device_backed = true): Base(size, size[0], _device_backed) {
|
||||
|
||||
}
|
||||
|
||||
/// Constructs a HostMatrixColumnMajor given size, layout, and leading dimension
|
||||
HostMatrixColumnMajor(TensorCoord const& size, Index ldm, bool _device_backed = true) {
|
||||
this->reset(make_Coord(ldm, 1), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Resizes a matrix
|
||||
void resize(MatrixCoord const &size, int ldm = 0, bool _device_backed = true) {
|
||||
this->reset(ldm, size, _device_backed);
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return MatrixLayout::kColumnMajor;
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
return this->stride(0);
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with row-major layout
|
||||
template <
|
||||
typename T
|
||||
>
|
||||
class HostMatrixRowMajor :
|
||||
public HostTensor<T, 2, MatrixLayout::RowMajor, 2, int, long long> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensor of rank=2 with contiguous layout
|
||||
typedef HostTensor<T, 2, MatrixLayout::RowMajor, 2, int, long long> Base;
|
||||
|
||||
/// Tensor coordinate
|
||||
typedef typename Base::TensorCoord TensorCoord;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrixRowMajor() { }
|
||||
|
||||
/// Constructs a HostTensor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrixRowMajor(TensorCoord const& size, bool _device_backed = true) {
|
||||
this->reset(make_Coord(size[1], 1), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Constructs a HostTensor given size, layout, and leading dimension
|
||||
HostMatrixRowMajor(TensorCoord const& size, Index ldm, bool _device_backed = true) {
|
||||
this->reset(make_Coord(ldm, 1), size, _device_backed);
|
||||
}
|
||||
|
||||
/// Resizes a matrix
|
||||
void resize(MatrixCoord const &size, int ldm = 0, bool _device_backed = true) {
|
||||
this->reset(ldm, size, _device_backed);
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return MatrixLayout::kRowMajor;
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
return this->stride(0);
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,205 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 HostMatrix is a helper to define a HostTensor of rank=2 with a contiguous layout.
|
||||
|
||||
See tools/util/host_tensor.h for more details.
|
||||
*/
|
||||
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "tools/util/host_tensor.h"
|
||||
|
||||
#include "tools/util/reference/host/gemm.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with contiguous layout
|
||||
template <
|
||||
typename T
|
||||
>
|
||||
class HostMatrixView :
|
||||
public HostTensorView<T, 2, MatrixLayout::ContiguousLayout, 3, int> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensor of rank=2 with contiguous layout
|
||||
typedef HostTensorView<T, 2, MatrixLayout::ContiguousLayout, 3, int> Base;
|
||||
|
||||
/// Tensor coordinate
|
||||
typedef typename Base::TensorCoord TensorCoord;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
private:
|
||||
|
||||
/// Layout of contiguous matrix
|
||||
MatrixLayout::Kind layout_;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrixView(): layout_(MatrixLayout::kColumnMajor) { }
|
||||
|
||||
/// Constructs a HostTensor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrixView(TensorCoord const& size): layout_(MatrixLayout::kColumnMajor) {
|
||||
Index ldm = size[0];
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size);
|
||||
}
|
||||
|
||||
/// Constructs a HostTensor from size and layout - infers leading dimension
|
||||
HostMatrixView(TensorCoord const& size, MatrixLayout::Kind layout): layout_(layout) {
|
||||
Index ldm = (layout_ == MatrixLayout::kColumnMajor ? size[0] : size[1]);
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size);
|
||||
}
|
||||
|
||||
/// Constructs a HostTensor given size, layout, and leading dimension
|
||||
HostMatrixView(TensorCoord const& size, Index ldm, MatrixLayout::Kind layout): layout_(layout) {
|
||||
this->reset(MatrixLayout::ContiguousLayout::stride(layout_, ldm), size);
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
if (layout_ == MatrixLayout::kColumnMajor) {
|
||||
return this->stride(MatrixLayout::ContiguousLayout::kColumn);
|
||||
}
|
||||
else {
|
||||
return this->stride(MatrixLayout::ContiguousLayout::kRow);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return layout_;
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with column-major layout
|
||||
template <typename T>
|
||||
class HostMatrixViewColumnMajor :
|
||||
public HostTensorView<T, 2, MatrixLayout::ColumnMajor, 2, int, long long> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensorView of rank=2 with contiguous layout
|
||||
typedef HostTensorView<T, 2, MatrixLayout::ColumnMajor, 2, int, long long> Base;
|
||||
|
||||
/// Tensor coordinate
|
||||
typedef typename Base::TensorCoord TensorCoord;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrixViewColumnMajor() { }
|
||||
|
||||
/// Constructs a HostMatrixViewColumnMajor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrixViewColumnMajor(TensorCoord const& size): Base(size, size[0]) {
|
||||
|
||||
}
|
||||
|
||||
/// Constructs a HostMatrixViewColumnMajor given size, layout, and leading dimension
|
||||
HostMatrixViewColumnMajor(TensorCoord const& size, Index ldm) {
|
||||
this->reset(make_Coord(ldm, 1), size);
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return MatrixLayout::kColumnMajor;
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
return this->stride(0);
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to define a rank=2 host matrix with row-major layout
|
||||
template <typename T>
|
||||
class HostMatrixViewRowMajor :
|
||||
public HostTensorView<T, 2, MatrixLayout::RowMajor, 2, int, long long> {
|
||||
public:
|
||||
|
||||
/// Base class is a HostTensor of rank=2 with contiguous layout
|
||||
typedef HostTensorView<T, 2, MatrixLayout::RowMajor, 2, int, long long> Base;
|
||||
|
||||
/// Tensor coordinate
|
||||
typedef typename Base::TensorCoord TensorCoord;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
public:
|
||||
|
||||
/// Default ctor
|
||||
HostMatrixViewRowMajor() { }
|
||||
|
||||
/// Constructs a HostMatrixViewRowMajor from size. Assumes column-major and infers leading dimension
|
||||
HostMatrixViewRowMajor(TensorCoord const& size): Base(size, size[1]) {
|
||||
|
||||
}
|
||||
|
||||
/// Constructs a HostMatrixViewRowMajor given size, layout, and leading dimension
|
||||
HostMatrixViewRowMajor(TensorCoord const& size, Index ldm) {
|
||||
this->reset(make_Coord(ldm, 1), size);
|
||||
}
|
||||
|
||||
/// Returns contiguous matrix layout kind
|
||||
MatrixLayout::Kind get_layout() const {
|
||||
return MatrixLayout::kRowMajor;
|
||||
}
|
||||
|
||||
/// Gets the leading dimension of the matrix
|
||||
Index leading_dim() const {
|
||||
return this->stride(0);
|
||||
}
|
||||
|
||||
/// Returns size as a MatrixCoord
|
||||
MatrixCoord size() const {
|
||||
return MatrixCoord(Base::size());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
+215
-192
@@ -25,51 +25,126 @@
|
||||
#pragma once
|
||||
|
||||
/*! \file
|
||||
\brief Template class to perform computations on tensors and manage memory.
|
||||
\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 device_{data, ref, view} for accessing device memory allocations.
|
||||
|
||||
See cutlass/tensor_ref.h, cutlass/tensor_view.h, and tools/util/host_tensor_view.h for more details.
|
||||
*/
|
||||
|
||||
#include <cutlass/cutlass.h>
|
||||
#include <cutlass/matrix_traits.h>
|
||||
#include <tools/util/device_memory.h>
|
||||
#include <tools/util/host_tensor_view.h>
|
||||
#include <tools/util/type_traits.h>
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/matrix_traits.h"
|
||||
#include "cutlass/tensor_ref.h"
|
||||
#include "tools/util/device_memory.h"
|
||||
#include "tools/util/host_tensor_view.h"
|
||||
#include "tools/util/type_traits.h"
|
||||
#include <vector>
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
template <typename T, bool DeviceBacked_ = true>
|
||||
class HostTensor : public HostTensorView<T> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Host tensor
|
||||
template <
|
||||
/// Scalar data type (may be mapped to compatible types for use on host and device)
|
||||
typename T,
|
||||
/// Rank of logical tensor
|
||||
int Rank_ = 4,
|
||||
/// Maps a Coord<Rank_> in the logical tensor index space to the internal n-D array
|
||||
typename MapFunc_ = IdentityTensorMapFunc<Rank_>,
|
||||
/// Rank of internal n-D array
|
||||
int StorageRank_ = MapFunc_::kStorageRank,
|
||||
/// Index type used for coordinates
|
||||
typename Index_ = int,
|
||||
/// Index type used for offsets and pointer differences
|
||||
typename LongIndex_ = long long
|
||||
>
|
||||
class HostTensor : public HostTensorView<
|
||||
typename TypeTraits<T>::host_type,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> {
|
||||
public:
|
||||
/// Type used for host-side allocations
|
||||
typedef typename TypeTraits<T>::host_type HostType;
|
||||
|
||||
/// Type used for device-side allocations
|
||||
typedef typename TypeTraits<T>::device_type DeviceType;
|
||||
|
||||
/// Base class
|
||||
typedef HostTensorView<T> Base;
|
||||
|
||||
/// If true, allocates device side memory
|
||||
static bool const DeviceBacked = DeviceBacked_;
|
||||
|
||||
/// Rank of tensor
|
||||
static int const Rank = Base::Rank;
|
||||
typedef HostTensorView<
|
||||
typename TypeTraits<T>::host_type,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> Base;
|
||||
|
||||
/// Type used to compute the offset of an element to the base of a tensor
|
||||
typedef typename Base::Offset_t Offset_t;
|
||||
|
||||
/// Tensor reference to host memory
|
||||
typedef typename Base::TensorRef_t TensorRef_t;
|
||||
typedef LongIndex_ LongIndex;
|
||||
|
||||
/// Tensor reference to device memory
|
||||
typedef TensorRef<DeviceType, TensorRef_t::Rank> DeviceTensorRef;
|
||||
typedef typename cutlass::TensorRef<
|
||||
DeviceType,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> DeviceTensorRef;
|
||||
|
||||
/// Tensor reference to constant device memory
|
||||
typedef TensorRef<DeviceType const, TensorRef_t::Rank> ConstDeviceTensorRef;
|
||||
typedef typename DeviceTensorRef::ConstTensorRef ConstDeviceTensorRef;
|
||||
|
||||
/// Coordinate into tensor
|
||||
typedef typename Base::Coord_t Coord_t;
|
||||
/// TensorView to device memory
|
||||
typedef TensorView<
|
||||
DeviceType,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> DeviceTensorView;
|
||||
|
||||
/// Tensor reference to constant device memory
|
||||
typedef typename DeviceTensorView::ConstTensorView ConstDeviceTensorView;
|
||||
|
||||
/// Tensor reference to host memory
|
||||
typedef typename Base::TensorRef TensorRef;
|
||||
|
||||
/// Tensor view to host memory
|
||||
typedef TensorView<
|
||||
typename TypeTraits<T>::host_type,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> HostTensorView;
|
||||
|
||||
/// Tensor view to host memory
|
||||
typedef typename HostTensorView::ConstTensorView ConstHostTensorView;
|
||||
|
||||
/// Coordinate in logical tensor space
|
||||
typedef typename TensorRef::TensorCoord TensorCoord;
|
||||
|
||||
/// Coordinate in storage n-D array
|
||||
typedef typename TensorRef::StorageCoord StorageCoord;
|
||||
|
||||
/// Stride vector in storage coordinate space
|
||||
/// Least significant stride is = 1 and not stored
|
||||
typedef typename TensorRef::StrideVector StrideVector;
|
||||
|
||||
/// Rank of internal storage.
|
||||
static int const kStorageRank = Base::kStorageRank;
|
||||
|
||||
private:
|
||||
|
||||
/// Host-side memory allocation
|
||||
std::vector<T> host_;
|
||||
std::vector<HostType> host_;
|
||||
|
||||
/// Device-side memory
|
||||
cutlass::device_memory::allocation<DeviceType> device_;
|
||||
@@ -82,232 +157,173 @@ class HostTensor : public HostTensorView<T> {
|
||||
/// Default constructor
|
||||
HostTensor() {}
|
||||
|
||||
/// Constructs a Tensor_view from stride and size
|
||||
HostTensor(Coord_t const& _stride, Coord_t const& _size) { reset(_stride, _size); }
|
||||
|
||||
/// Constructs a HostTensor from size - infers strides
|
||||
HostTensor(Coord_t const& _size) {
|
||||
Coord_t _stride = make_Coord(
|
||||
_size.at(2) * _size.at(1) * _size.at(0), _size.at(1) * _size.at(0), _size.at(0), 1);
|
||||
reset(_stride, _size);
|
||||
/// Constructor for resizing the least significant rank
|
||||
HostTensor(Index_ size_1D, bool device_backed = true) {
|
||||
this->resize(size_1D, device_backed);
|
||||
}
|
||||
|
||||
/// Returns the number of elements needed to back vector
|
||||
size_t capacity() { return Base::capacity(); }
|
||||
/// Helper to construct from pointer, stride, and size
|
||||
HostTensor(
|
||||
StorageCoord const &_stride,
|
||||
TensorCoord const& _size,
|
||||
bool _device_backed = true
|
||||
) {
|
||||
|
||||
/// Returns true if the Tensor_view is bound to some memory
|
||||
bool good() const { return Base::good(); }
|
||||
this->reset(_stride, _size);
|
||||
}
|
||||
|
||||
/// Clears the HostTensor allocation to size/capacity = 0
|
||||
void reset() {
|
||||
host_.clear();
|
||||
device_.reset();
|
||||
Base::reset();
|
||||
}
|
||||
|
||||
/// Helper to resize the least significant rank
|
||||
void resize(
|
||||
Index_ size_1D,
|
||||
bool _device_backed = true) {
|
||||
|
||||
TensorCoord _size;
|
||||
_size[Base::kRank - 1] = size_1D;
|
||||
for (int i = 0; i < Base::kRank - 1; ++i) {
|
||||
_size[i] = 1;
|
||||
}
|
||||
StorageCoord _stride;
|
||||
_stride[Base::kStorageRank - 1] = 1;
|
||||
for (int i = 0; i < Base::kStorageRank - 1; ++i) {
|
||||
_stride[i] = size_1D;
|
||||
}
|
||||
this->reset(_stride, _size, _device_backed);
|
||||
}
|
||||
|
||||
/// Updates the reference and size of a Tensor_view object
|
||||
void reset(Coord_t const& _stride, Coord_t const& _size) {
|
||||
size_t _capacity = _size.at(0) * _stride.at(0);
|
||||
void reset(
|
||||
StorageCoord const& stride,
|
||||
TensorCoord const& size,
|
||||
bool _device_backed = true) {
|
||||
|
||||
// Construct a temporary TensorView so we can calculate the new capacity
|
||||
size_t _capacity = Base(nullptr, stride, size).capacity();
|
||||
|
||||
// Allocate memory
|
||||
DeviceType* _device_memory = nullptr;
|
||||
if (DeviceBacked) {
|
||||
if (_device_backed) {
|
||||
_device_memory = cutlass::device_memory::allocate<DeviceType>(_capacity);
|
||||
}
|
||||
|
||||
host_.clear();
|
||||
host_.resize(_capacity);
|
||||
for (size_t i = 0; i < _capacity; ++i) {
|
||||
host_[i] = T((int)0xdeadbeef);
|
||||
}
|
||||
device_.reset(_device_memory, _capacity);
|
||||
|
||||
Base::reset(TensorRef_t(host_.data(), _stride), _size);
|
||||
Base::reset(TensorRef(host_.data(), stride), size);
|
||||
}
|
||||
|
||||
/// Initializes the host tensor as a matrix
|
||||
void resize_matrix(int rows, int columns, MatrixLayout::Kind layout) {
|
||||
bool col_major = (layout == MatrixLayout::kColumnMajor);
|
||||
int ldm = (col_major ? rows : columns);
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef host_ref() { return Base::ref(); }
|
||||
|
||||
Coord_t stride = make_Coord(rows * columns, col_major ? 1 : ldm, col_major ? ldm : 1, 1);
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef host_ref() const { return Base::ref(); }
|
||||
|
||||
Coord_t size = make_Coord(1, rows, columns, 1);
|
||||
|
||||
reset(stride, size);
|
||||
/// Accesses the tensor reference pointing to data
|
||||
DeviceTensorRef device_ref() const {
|
||||
return DeviceTensorRef(device_data(), this->stride());
|
||||
}
|
||||
|
||||
/// Simplifies resizing the host tensor
|
||||
void resize(int elements) { resize_matrix(1, elements, MatrixLayout::kColumnMajor); }
|
||||
/// Accesses the tensor reference pointing to data
|
||||
HostTensorView host_view() {
|
||||
return HostTensorView(host_data(), this->stride(), this->size());
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
ConstHostTensorView host_view() const {
|
||||
return HostTensorView(host_data(), this->stride(), this->size());
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
DeviceTensorView device_view() const {
|
||||
return DeviceTensorView(device_data(), this->stride(), this->size());
|
||||
}
|
||||
|
||||
/// Gets pointer to host data
|
||||
T const* host_data() const { return &host_[0]; }
|
||||
|
||||
/// Gets pointer to host data
|
||||
T* host_data() { return &host_[0]; }
|
||||
HostType * host_data() { return host_.data(); }
|
||||
|
||||
/// Gets pointer to device data
|
||||
DeviceType* device_data() const { return device_.get(); }
|
||||
DeviceType* device_data() { return device_.get(); }
|
||||
|
||||
/// Gets pointer to host data
|
||||
HostType const * host_data() const { return host_.data(); }
|
||||
|
||||
/// Gets pointer to device data
|
||||
DeviceType * device_data() const { return device_.get(); }
|
||||
|
||||
/// Returns true if device memory is allocated
|
||||
bool device_backed() const {
|
||||
return device_.get();
|
||||
}
|
||||
|
||||
/// Copies data from device to host
|
||||
void sync_host() {
|
||||
if (DeviceBacked) {
|
||||
if (device_.get()) {
|
||||
device_memory::copy_to_host(
|
||||
host_.data(), reinterpret_cast<T const*>(device_.get()), host_.size());
|
||||
host_.data(), reinterpret_cast<HostType const*>(device_.get()), host_.size());
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies data from host to device
|
||||
void sync_device() {
|
||||
if (DeviceBacked) {
|
||||
if (device_.get()) {
|
||||
device_memory::copy_to_device(
|
||||
device_.get(), reinterpret_cast<DeviceType const*>(host_.data()), host_.size());
|
||||
device_.get(),
|
||||
reinterpret_cast<DeviceType const*>(host_.data()),
|
||||
host_.size());
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy data from a caller-supplied device pointer
|
||||
void copy_to_host(DeviceType const *ptr_device) {
|
||||
/// Copy data from a caller-supplied device pointer into host memory
|
||||
void copy_to_host(DeviceType const* ptr_device) {
|
||||
device_memory::copy_to_host(
|
||||
host_.data(), reinterpret_cast<T const *>(ptr_device), host_.size());
|
||||
host_.data(), reinterpret_cast<HostType const*>(ptr_device), host_.size());
|
||||
}
|
||||
|
||||
/// Copies data to a caller-supplied device pointer
|
||||
void copy_to_device(DeviceType *ptr_device) {
|
||||
/// Copies device-to-device
|
||||
void copy_to_device(DeviceType* ptr_device) {
|
||||
device_memory::copy_to_device(
|
||||
ptr_device, reinterpret_cast<DeviceType const *>(host_.data()), host_.size());
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef_t& host_ref() { return Base::ref(); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef_t const& host_ref() const { return Base::ref(); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
DeviceTensorRef device_ref() const { return DeviceTensorRef(device_data(), stride()); }
|
||||
|
||||
/// Returns a tensor ref to constant memory on the device
|
||||
ConstDeviceTensorRef const_device_ref() const {
|
||||
return ConstDeviceTensorRef(device_data(), stride());
|
||||
}
|
||||
|
||||
/// Accesses the size
|
||||
Coord_t const& size() const { return Base::size(); }
|
||||
|
||||
/// Accesses the size
|
||||
int size(int dim) const { return Base::size(dim); }
|
||||
|
||||
/// Accesses the size
|
||||
Coord_t const& stride() const { return Base::stride(); }
|
||||
|
||||
/// Accesses the size
|
||||
int stride(int dim) const { return Base::stride(dim); }
|
||||
|
||||
/// Returns the index of an element
|
||||
Offset_t offset(Coord_t const& coord) const { return Base::offset(coord); }
|
||||
|
||||
/// Determines whether a location is within a tensor
|
||||
bool contains(Coord_t const& coord) const { return Base::contains(coord); }
|
||||
|
||||
/// Element-wise accessor
|
||||
T& at(Coord_t const& coord) const { return Base::at(coord); }
|
||||
|
||||
/// Element-wise accessor
|
||||
T& operator[](Coord_t const& coord) { return at(coord); }
|
||||
|
||||
/// Element-wise accessor with basic offset
|
||||
T& at(int idx) const { return Base::at(idx); }
|
||||
|
||||
/// Returns a Tensor_view given location and size quantities
|
||||
TensorView<T> subview(Coord_t const& _location, Coord_t _size) const {
|
||||
return Base::subview(_location, _size);
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operation
|
||||
template <typename F>
|
||||
void elementwise_in_place(F& op, int dim = 0, Offset_t dst_offset_base = 0) {
|
||||
Base::elementwise_in_place(op, dim, dst_offset_base);
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operator, supplying the logical
|
||||
/// coordinate within the tensor as an argument
|
||||
template <typename F>
|
||||
void elementwise_stream(F& op, int dim = 0, Offset_t dst_offset_base = 0) {
|
||||
Base::elementwise_stream(op, dim, dst_offset_base);
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operator, supplying the logical
|
||||
/// coordinate within the tensor as an argument
|
||||
template <typename F>
|
||||
void elementwise_generate(F& op,
|
||||
int dim = 0,
|
||||
Offset_t dst_offset_base = 0,
|
||||
Coord_t coord = Coord_t(0)) {
|
||||
Base::elementwise_generate(op, dim, dst_offset_base, coord);
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a binary operation
|
||||
template <typename Src, typename F>
|
||||
bool elementwise_in_place(F& op,
|
||||
int dim,
|
||||
TensorView<Src> const& tensor,
|
||||
Offset_t dst_offset_base = 0,
|
||||
Offset_t src_offset_base = 0) {
|
||||
return Base::elementwise_in_place(op, dim, tensor, dst_offset_base, src_offset_base);
|
||||
ptr_device, reinterpret_cast<DeviceType const*>(host_.data()), host_.size());
|
||||
}
|
||||
|
||||
/// Accumulate in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator+=(TensorView<Src> const& tensor) {
|
||||
template <typename SrcTensorView>
|
||||
HostTensor& operator+=(SrcTensorView const& tensor) {
|
||||
Base::operator+=(tensor);
|
||||
sync_device();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtract in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator-=(TensorView<Src> const& tensor) {
|
||||
template <typename SrcTensorView>
|
||||
HostTensor& operator-=(SrcTensorView const& tensor) {
|
||||
Base::operator-=(tensor);
|
||||
sync_device();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator*=(TensorView<Src> const& tensor) {
|
||||
template <typename SrcTensorView>
|
||||
HostTensor& operator*=(SrcTensorView const& tensor) {
|
||||
Base::operator*=(tensor);
|
||||
sync_device();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Divide in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator/=(TensorView<Src> const& tensor) {
|
||||
template <typename SrcTensorView>
|
||||
HostTensor& operator/=(SrcTensorView const& tensor) {
|
||||
Base::operator/=(tensor);
|
||||
sync_device();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// equality with epsilon tolerance
|
||||
bool equals(TensorView<T> const& tensor, T epsilon) const {
|
||||
return Base::equals(tensor, epsilon);
|
||||
}
|
||||
|
||||
/// equality with ulps tolerance
|
||||
bool bit_equals(TensorView<T> const& tensor, long long ulps_threshold = 0) {
|
||||
return Base::bit_equals(tensor, ulps_threshold);
|
||||
}
|
||||
|
||||
/// Computes general matrix product among select dimensions of a tensor
|
||||
/// Assumes:
|
||||
/// D: number of independent GEMMs to compute
|
||||
/// H: height of matrix
|
||||
/// W: width of matrix
|
||||
template <
|
||||
/// Data type of A matrix elements
|
||||
typename A,
|
||||
/// Data type of B matrix elements
|
||||
typename B,
|
||||
/// Data type of "compute" type (i.e. accumulator)
|
||||
typename Ctype,
|
||||
/// Data type of scale factors
|
||||
typename Stype>
|
||||
void gemm(TensorView<A> const& tensor_a, TensorView<B> const& tensor_b, Stype alpha, Stype beta) {
|
||||
Base::template gemm<A, B, Ctype, Stype>(tensor_a, tensor_b, alpha, beta);
|
||||
}
|
||||
|
||||
/// Fills with random data
|
||||
template <typename Gen>
|
||||
void fill_random(Gen generator) {
|
||||
@@ -335,31 +351,38 @@ class HostTensor : public HostTensorView<T> {
|
||||
}
|
||||
|
||||
/// computes elements as a linear combination of their coordinates
|
||||
void fill_linear(Coord_t v, T offset = T(0)) {
|
||||
void fill_linear(TensorCoord v, HostType offset = HostType(0)) {
|
||||
Base::fill_linear(v, offset);
|
||||
sync_device();
|
||||
}
|
||||
|
||||
/// computes elements as a linear combination of their coordinates
|
||||
void fill_sequential(T v = T(1), T offset = T(0)) {
|
||||
void fill_sequential(HostType v = HostType(1), HostType offset = HostType(0)) {
|
||||
Base::fill_sequential(v, offset);
|
||||
sync_device();
|
||||
}
|
||||
|
||||
/// fills with a value
|
||||
void fill(T val = T(0)) {
|
||||
void fill(HostType val = HostType(0)) {
|
||||
Base::fill(val);
|
||||
sync_device();
|
||||
}
|
||||
|
||||
/// Copies from external data source and performs type conversion
|
||||
template <typename Src>
|
||||
void fill(TensorView<Src> const& tensor) {
|
||||
/// copies from external data source and performs type conversion
|
||||
template <
|
||||
typename SrcType,
|
||||
typename SrcMapFunc_,
|
||||
int SrcStorageRank_,
|
||||
typename SrcIndex_,
|
||||
typename SrcLongIndex_
|
||||
>
|
||||
void fill(
|
||||
TensorView<SrcType, Base::kRank, SrcMapFunc_, SrcStorageRank_, SrcIndex_, SrcLongIndex_> const& tensor) {
|
||||
Base::fill(tensor);
|
||||
sync_device();
|
||||
}
|
||||
|
||||
/// Computes the norm of the matrix in double-precision
|
||||
double norm() const { return Base::norm(); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
|
||||
+199
-246
@@ -23,45 +23,77 @@
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/*! \file
|
||||
\brief Host-side implementation of useful operations
|
||||
\brief Host-side implementation of basic tensor operations.
|
||||
|
||||
See cutlass/tensor_ref.h and cutlass/tensor_view.h for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/cutlass.h>
|
||||
#include <cutlass/tensor_view.h>
|
||||
#include <tools/util/type_traits.h>
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
#include "tools/util/type_traits.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename SrcType, typename DstType>
|
||||
struct Cast {
|
||||
static inline DstType apply(SrcType src) { return static_cast<DstType>(src); };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Cast<float, int8_t> {
|
||||
static inline int8_t apply(float src) {
|
||||
return static_cast<int8_t>(fmaxf(-128.f, fminf(127.f, src)));
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Cast<float, uint8_t> {
|
||||
static inline uint8_t apply(float src) {
|
||||
return static_cast<uint8_t>(fmaxf(0.f, fminf(255.f, src)));
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
class HostTensorView : public TensorView<T> {
|
||||
template <
|
||||
/// Data type of element stored within tensor
|
||||
typename Storage_,
|
||||
/// Rank of logical tensor
|
||||
int Rank_ = 4,
|
||||
/// Maps a Coord<Rank_> in the logical tensor index space to the internal n-D array
|
||||
typename MapFunc_ = IdentityTensorMapFunc<Rank_>,
|
||||
/// Rank of internal n-D array
|
||||
int StorageRank_ = Rank_,
|
||||
/// Index type used for coordinates
|
||||
typename Index_ = int,
|
||||
/// Index type used for offsets and pointer differences
|
||||
typename LongIndex_ = long long
|
||||
>
|
||||
class HostTensorView :
|
||||
public TensorView<Storage_, Rank_, MapFunc_, StorageRank_, Index_, LongIndex_> {
|
||||
public:
|
||||
/// Base class
|
||||
typedef TensorView<T> TensorView_t;
|
||||
typedef TensorView<Storage_, Rank_, MapFunc_, StorageRank_, Index_, LongIndex_> Base;
|
||||
|
||||
/// Storage type
|
||||
typedef typename Base::Storage Storage;
|
||||
|
||||
/// Alias for underlying TensorRef
|
||||
typedef typename Base::TensorRef TensorRef;
|
||||
|
||||
/// Index type
|
||||
typedef typename Base::Index Index;
|
||||
|
||||
/// Coordinate in logical tensor space
|
||||
typedef typename TensorRef::TensorCoord TensorCoord;
|
||||
|
||||
/// Coordinate in storage n-D array
|
||||
typedef typename TensorRef::StorageCoord StorageCoord;
|
||||
|
||||
/// Stride vector in storage coordinate space
|
||||
/// Least significant stride is = 1 and not stored
|
||||
typedef typename TensorRef::StrideVector StrideVector;
|
||||
|
||||
/// Long index type for pointer offsets
|
||||
typedef typename Base::LongIndex LongIndex;
|
||||
|
||||
/// Rank of tensor index space
|
||||
static int const kRank = Base::kRank;
|
||||
|
||||
//
|
||||
// Definitions included for backwards compatibility - These will be remmoved
|
||||
// in the next major release.
|
||||
//
|
||||
|
||||
/// Base class
|
||||
typedef Base TensorView_t;
|
||||
|
||||
//
|
||||
// These definitions are meaningful for rank=4 tensors.
|
||||
//
|
||||
|
||||
/// Convention: depth is the first dimension
|
||||
static int const Dim_D = 0;
|
||||
@@ -75,19 +107,8 @@ class HostTensorView : public TensorView<T> {
|
||||
/// Convention: channel is the second dimension
|
||||
static int const Dim_C = 3;
|
||||
|
||||
/// Rank of tensor
|
||||
static int const Rank = TensorView_t::Rank;
|
||||
|
||||
/// Type used to compute the offset of an element to the base of a tensor
|
||||
typedef typename TensorView_t::Offset_t Offset_t;
|
||||
|
||||
/// Reference and stride
|
||||
typedef typename TensorView_t::TensorRef_t TensorRef_t;
|
||||
|
||||
/// Coordinate into tensor
|
||||
typedef typename TensorView_t::Coord_t Coord_t;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Device and Host Methods
|
||||
//
|
||||
@@ -95,91 +116,87 @@ class HostTensorView : public TensorView<T> {
|
||||
/// Default constructor
|
||||
HostTensorView() {}
|
||||
|
||||
/// Constructs a Tensor_view from a TensorRef and size
|
||||
HostTensorView(TensorRef_t const& _ref, Coord_t const& _size) : TensorView_t(_ref, _size) {}
|
||||
/// Helper to construct from pointer, stride, and size
|
||||
HostTensorView(
|
||||
Storage_ *_ptr,
|
||||
StrideVector const &_stride,
|
||||
TensorCoord const& _size
|
||||
) : Base(TensorRef(_ptr, _stride), _size) {}
|
||||
|
||||
/// Accesses the size
|
||||
Coord_t const& size() const { return TensorView_t::size(); }
|
||||
/// Helper to construct from pointer, stride, and size
|
||||
HostTensorView(
|
||||
Storage_ *_ptr,
|
||||
StorageCoord const &_stride,
|
||||
TensorCoord const& _size
|
||||
) : Base(TensorRef(_ptr, _stride), _size) {}
|
||||
|
||||
/// Accesses the size of a specified dimension
|
||||
int size(int dim) const { return size().at(dim); }
|
||||
|
||||
/// Accesses the stride
|
||||
Coord_t const& stride() const { return TensorView_t::stride(); }
|
||||
|
||||
/// Accesses the stride along a specified dimension
|
||||
int stride(int dim) const { return stride().at(dim); }
|
||||
|
||||
/// Returns the number of scalar elements needed to store tensor
|
||||
size_t capacity() const { return size(3) * stride(3) * stride(2) * stride(1) * stride(0); }
|
||||
|
||||
/// Returns true if the Tensor_view is bound to some memory
|
||||
bool good() const { return TensorView_t::good(); }
|
||||
|
||||
/// Updates the reference and size of a TensorView object
|
||||
void reset(TensorRef_t const& _ref = TensorRef_t(0), Coord_t const& _size = Coord_t()) {
|
||||
return TensorView_t::reset(_ref, _size);
|
||||
}
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef_t& ref() { return TensorView_t::ref(); }
|
||||
|
||||
/// Accesses the tensor reference pointing to data
|
||||
TensorRef_t const& ref() const { return TensorView_t::ref(); }
|
||||
/// Constructs a Tensor_view from a TensorRef and size assuming dense packing
|
||||
HostTensorView(
|
||||
TensorRef const& _ref,
|
||||
TensorCoord const& _size) : Base(_ref, _size) {}
|
||||
|
||||
/// Assigns a tensor view
|
||||
HostTensorView& operator=(TensorView_t const& _tensor) {
|
||||
reset(_tensor.ref(), _tensor.size());
|
||||
HostTensorView& operator=(Base const& _tensor) {
|
||||
this->reset(_tensor.ref(), _tensor.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the index of an element
|
||||
Offset_t offset(Coord_t const& coord) const { return TensorView_t::offset(coord); }
|
||||
/// Returns a TensorView offset by a given amount
|
||||
CUTLASS_HOST_DEVICE
|
||||
HostTensorView operator+(TensorCoord const& b) const {
|
||||
HostTensorView result(*this);
|
||||
result.add_pointer_offset(this->offset(b));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Determines whether a location is within a tensor
|
||||
bool contains(Coord_t const& coord) const { return TensorView_t::contains(coord); }
|
||||
/// Returns a TensorRef offset by a given amount
|
||||
CUTLASS_HOST_DEVICE
|
||||
HostTensorView& operator+=(TensorCoord const& b) {
|
||||
this->add_pointer_offset(this->offset(b));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Element-wise accessor
|
||||
T& at(Coord_t const& coord) const { return TensorView_t::at(coord); }
|
||||
/// Returns a TensorRef offset by a given amount
|
||||
CUTLASS_HOST_DEVICE
|
||||
HostTensorView operator-(TensorCoord const& b) const {
|
||||
TensorRef result(*this);
|
||||
result.add_pointer_offset(-this->offset(b));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Element-wise accessor
|
||||
T& operator[](Coord_t const& coord) const { return at(coord); }
|
||||
|
||||
/// Accesses an element with a raw offset
|
||||
T& at(int idx) const { return TensorView_t::at(idx); }
|
||||
|
||||
/// Accesses an element with a raw offset
|
||||
T& operator[](int idx) const { return at(idx); }
|
||||
|
||||
/// Returns a Tensor_view given location and size quantities
|
||||
TensorView_t subview(Coord_t const& location, Coord_t size) const {
|
||||
return TensorView_t::subview(location, size);
|
||||
/// Returns a TensorRef offset by a given amount
|
||||
CUTLASS_HOST_DEVICE
|
||||
HostTensorView& operator-=(TensorCoord const& b) {
|
||||
this->add_pointer_offset(-this->offset(b));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operation in place
|
||||
template <typename F>
|
||||
void elementwise_in_place(F& op, int dim = 0, Offset_t dst_offset_base = 0) {
|
||||
Offset_t dst_offset = dst_offset_base;
|
||||
void elementwise_in_place(F& op, int dim = 0, TensorCoord const &start_coord = TensorCoord()) {
|
||||
|
||||
for (int idx = 0; idx < size(dim); ++idx, dst_offset += stride(dim)) {
|
||||
if (dim < Rank - 1) {
|
||||
elementwise_in_place(op, dim + 1, dst_offset);
|
||||
TensorCoord coord(start_coord);
|
||||
for (int idx = 0; idx < this->size(dim); ++idx) {
|
||||
coord[dim] = idx;
|
||||
if (dim < kRank - 1) {
|
||||
elementwise_in_place(op, dim + 1, coord);
|
||||
} else {
|
||||
op(ref().data()[dst_offset]);
|
||||
op(this->at(coord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operator with no arguments
|
||||
template <typename F>
|
||||
void elementwise_stream(F& op, int dim = 0, Offset_t dst_offset_base = 0) {
|
||||
Offset_t dst_offset = dst_offset_base;
|
||||
void elementwise_stream(F& op, int dim = 0, TensorCoord const &start_coord = TensorCoord()) {
|
||||
|
||||
for (int idx = 0; idx < size(dim); ++idx, dst_offset += stride(dim)) {
|
||||
if (dim < Rank - 1) {
|
||||
elementwise_stream(op, dim + 1, dst_offset);
|
||||
TensorCoord coord(start_coord);
|
||||
for (int idx = 0; idx < this->size(dim); ++idx) {
|
||||
coord[dim] = idx;
|
||||
if (dim < kRank - 1) {
|
||||
elementwise_stream(op, dim + 1, coord);
|
||||
} else {
|
||||
ref().data()[dst_offset] = op();
|
||||
this->at(coord) = op();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,61 +206,56 @@ class HostTensorView : public TensorView<T> {
|
||||
template <typename F>
|
||||
void elementwise_generate(F& op,
|
||||
int dim = 0,
|
||||
Offset_t dst_offset_base = 0,
|
||||
Coord_t coord = Coord_t(0)) {
|
||||
Offset_t dst_offset = dst_offset_base;
|
||||
TensorCoord const & start_coord = TensorCoord()) {
|
||||
|
||||
for (int idx = 0; idx < size(dim); ++idx, dst_offset += stride(dim)) {
|
||||
coord.at(dim) = idx;
|
||||
|
||||
if (dim < Rank - 1) {
|
||||
elementwise_generate(op, dim + 1, dst_offset, coord);
|
||||
TensorCoord coord(start_coord);
|
||||
for (int idx = 0; idx < this->size(dim); ++idx) {
|
||||
coord[dim] = idx;
|
||||
if (dim < kRank - 1) {
|
||||
elementwise_generate(op, dim + 1, coord);
|
||||
} else {
|
||||
ref().data()[dst_offset] = op(coord);
|
||||
this->at(coord) = op(coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a unary operator, supplying the logical
|
||||
/// coordinate within the tensor as an argument
|
||||
/// coordinate within the tensor as an argument. Mutable.
|
||||
template <typename F>
|
||||
void elementwise_visit(F& op,
|
||||
int dim = 0,
|
||||
Offset_t dst_offset_base = 0,
|
||||
Coord_t coord = Coord_t(0)) const {
|
||||
Offset_t dst_offset = dst_offset_base;
|
||||
TensorCoord const & start_coord = TensorCoord()) const {
|
||||
|
||||
for (int idx = 0; idx < size(dim); ++idx, dst_offset += stride(dim)) {
|
||||
coord.at(dim) = idx;
|
||||
TensorCoord coord(start_coord);
|
||||
for (int idx = 0; idx < this->size(dim); ++idx) {
|
||||
coord[dim] = idx;
|
||||
|
||||
if (dim < Rank - 1) {
|
||||
elementwise_visit(op, dim + 1, dst_offset, coord);
|
||||
if (dim < kRank - 1) {
|
||||
elementwise_visit(op, dim + 1, coord);
|
||||
} else {
|
||||
op(ref().data()[dst_offset], coord);
|
||||
op(this->at(coord), coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recurses through all dimensions and applies a binary operation
|
||||
template <typename Src, typename F>
|
||||
template <typename F, typename SrcTensorView>
|
||||
bool elementwise_in_place(F& op,
|
||||
TensorView<Src> const& tensor,
|
||||
SrcTensorView const& tensor,
|
||||
int dim = 0,
|
||||
Offset_t dst_offset_base = 0,
|
||||
Offset_t src_offset_base = 0) {
|
||||
Offset_t dst_offset = dst_offset_base;
|
||||
Offset_t src_offset = src_offset_base;
|
||||
TensorCoord const &start_coord = TensorCoord()) {
|
||||
|
||||
if (size().at(dim) != tensor.size().at(dim)) {
|
||||
if (this->size(dim) != tensor.size(dim)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < size(dim);
|
||||
++idx, dst_offset += stride(dim), src_offset += tensor.stride(dim)) {
|
||||
if (dim < Rank - 1) {
|
||||
elementwise_in_place(op, tensor, dim + 1, dst_offset, src_offset);
|
||||
TensorCoord coord(start_coord);
|
||||
for (int idx = 0; idx < this->size(dim); ++idx) {
|
||||
coord[dim] = idx;
|
||||
if (dim < kRank - 1) {
|
||||
elementwise_in_place(op, tensor, dim + 1, coord);
|
||||
} else {
|
||||
op(data()[dst_offset], tensor.data()[src_offset]);
|
||||
op(this->at(coord), tensor.at(coord));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,55 +264,55 @@ class HostTensorView : public TensorView<T> {
|
||||
|
||||
template <typename Src>
|
||||
struct LambdaBinaryAddition {
|
||||
void operator()(T& a, Src b) const { a += T(b); }
|
||||
void operator()(Storage_& a, Src b) const { a += Storage_(b); }
|
||||
};
|
||||
|
||||
template <typename Src>
|
||||
struct LambdaBinarySubtraction {
|
||||
void operator()(T& a, Src b) const { a -= T(b); }
|
||||
void operator()(Storage_& a, Src b) const { a -= Storage_(b); }
|
||||
};
|
||||
|
||||
template <typename Src>
|
||||
struct LambdaBinaryMultiplication {
|
||||
void operator()(T& a, Src b) const { a *= T(b); }
|
||||
void operator()(Storage_& a, Src b) const { a *= Storage_(b); }
|
||||
};
|
||||
|
||||
template <typename Src>
|
||||
struct LambdaBinaryDivision {
|
||||
void operator()(T& a, Src b) const { a /= T(b); }
|
||||
void operator()(Storage_& a, Src b) const { a /= Storage_(b); }
|
||||
};
|
||||
|
||||
/// Accumulate in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator+=(TensorView<Src> const& tensor) {
|
||||
LambdaBinaryAddition<Src> op;
|
||||
template <typename SrcTensorView>
|
||||
HostTensorView& operator+=(SrcTensorView const& tensor) {
|
||||
LambdaBinaryAddition<typename SrcTensorView::Storage> op;
|
||||
elementwise_in_place(op, tensor);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtract in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator-=(TensorView<Src> const& tensor) {
|
||||
LambdaBinarySubtraction<Src> op;
|
||||
template <typename SrcTensorView>
|
||||
HostTensorView& operator-=(SrcTensorView const& tensor) {
|
||||
LambdaBinarySubtraction<typename SrcTensorView::Storage> op;
|
||||
elementwise_in_place(op, tensor);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator*=(TensorView<Src> const& tensor) {
|
||||
LambdaBinaryMultiplication<Src> op;
|
||||
template <typename SrcTensorView>
|
||||
HostTensorView& operator*=(SrcTensorView const& tensor) {
|
||||
LambdaBinaryMultiplication<typename SrcTensorView::Storage> op;
|
||||
elementwise_in_place(op, tensor);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Divide in place
|
||||
template <typename Src>
|
||||
TensorView<T>& operator/=(TensorView<Src> const& tensor) {
|
||||
LambdaBinaryDivision<Src> op;
|
||||
template <typename SrcTensorView>
|
||||
HostTensorView& operator/=(SrcTensorView const& tensor) {
|
||||
LambdaBinaryDivision<typename SrcTensorView::Storage> op;
|
||||
elementwise_in_place(op, tensor);
|
||||
|
||||
return *this;
|
||||
@@ -309,19 +321,19 @@ class HostTensorView : public TensorView<T> {
|
||||
/// Comparison operator
|
||||
struct EqualsOperator {
|
||||
bool equal;
|
||||
T eps;
|
||||
Storage_ eps;
|
||||
|
||||
EqualsOperator(T _epsilon) : equal(true), eps(_epsilon) {}
|
||||
EqualsOperator(Storage_ _epsilon) : equal(true), eps(_epsilon) {}
|
||||
|
||||
void operator()(T a, T b) {
|
||||
if (std::abs(T(a - b)) > eps * std::max(std::abs(a), std::abs(b))) {
|
||||
void operator()(Storage_ a, Storage_ b) {
|
||||
if (std::abs(Storage_(a - b)) > eps * std::max(std::abs(a), std::abs(b))) {
|
||||
equal = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// equality with epsilon tolerance
|
||||
bool equals(TensorView<T> const& tensor, T epsilon) const {
|
||||
bool equals(Base const& tensor, Storage epsilon) const {
|
||||
EqualsOperator comparison_op(epsilon);
|
||||
bool equal_size = elementwise_in_place(comparison_op, tensor);
|
||||
|
||||
@@ -336,13 +348,13 @@ class HostTensorView : public TensorView<T> {
|
||||
|
||||
BitEqualsOperator(long long _ulps_threshold) : equal(true), eps(_ulps_threshold), index(0) {}
|
||||
|
||||
void operator()(T a, T b) {
|
||||
void operator()(Storage_ a, Storage_ b) {
|
||||
// convert bits to integers
|
||||
long long bits_a = 0;
|
||||
long long bits_b = 0;
|
||||
|
||||
*reinterpret_cast<T*>(&bits_a) = TypeTraits<T>::remove_negative_zero(a);
|
||||
*reinterpret_cast<T*>(&bits_b) = TypeTraits<T>::remove_negative_zero(b);
|
||||
*reinterpret_cast<Storage_*>(&bits_a) = TypeTraits<Storage_>::remove_negative_zero(a);
|
||||
*reinterpret_cast<Storage_*>(&bits_b) = TypeTraits<Storage_>::remove_negative_zero(b);
|
||||
|
||||
// compute diff
|
||||
long long ulps = bits_a - bits_b;
|
||||
@@ -354,85 +366,13 @@ class HostTensorView : public TensorView<T> {
|
||||
};
|
||||
|
||||
/// equality with ulps tolerance
|
||||
bool bit_equals(TensorView<T> const& tensor, long long ulps_threshold = 0) {
|
||||
bool bit_equals(Base const& tensor, long long ulps_threshold = 0) {
|
||||
BitEqualsOperator comparison_op(ulps_threshold);
|
||||
bool equal_size = elementwise_in_place(comparison_op, tensor);
|
||||
|
||||
return equal_size && comparison_op.equal;
|
||||
}
|
||||
|
||||
/// Gets naked pointer to data
|
||||
T* data() const { return TensorView_t::data(); }
|
||||
|
||||
/// Computes general matrix product among select dimensions of a tensor
|
||||
/// Assumes:
|
||||
/// D: number of independent GEMMs to compute
|
||||
/// H: height of matrix
|
||||
/// W: width of matrix
|
||||
/// C: "channels" of each element
|
||||
template <typename A, typename B, typename Ctype, typename Stype>
|
||||
void gemm(TensorView<A> const& tensor_a, TensorView<B> const& tensor_b, Stype alpha, Stype beta) {
|
||||
int const Batch = size(Dim_D);
|
||||
int const M = size(Dim_H);
|
||||
int const N = size(Dim_W);
|
||||
int const K = tensor_a.size(Dim_W);
|
||||
int const C = tensor_a.size(Dim_C);
|
||||
|
||||
// Sizes must match
|
||||
if (tensor_a.size(Dim_H) != M || tensor_b.size(Dim_W) != N || tensor_b.size(Dim_C) != C ||
|
||||
tensor_b.size(Dim_H) != K) {
|
||||
return;
|
||||
}
|
||||
|
||||
int const Mblock = 32;
|
||||
int const Nblock = 32;
|
||||
|
||||
for (int batch = 0; batch < Batch; ++batch) {
|
||||
for (int row_block = 0; row_block < M; row_block += Mblock) {
|
||||
for (int col_block = 0; col_block < N; col_block += Nblock) {
|
||||
Ctype accum[Mblock][Nblock];
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
accum[i][j] = Ctype(0);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (int channel = 0; channel < C; ++channel) {
|
||||
Ctype a(tensor_a.at(make_Coord(batch, row, k_block, channel)));
|
||||
Ctype b(tensor_b.at(make_Coord(batch, k_block, col, channel)));
|
||||
|
||||
accum[i][j] += a * b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < Mblock; i++) {
|
||||
int row = row_block + i;
|
||||
int col = col_block + j;
|
||||
|
||||
Coord_t coord = make_Coord(batch, row, col, 0);
|
||||
if (row < M && col < N) {
|
||||
at(coord) =
|
||||
Cast<Stype, T>::apply(alpha * Stype(accum[i][j]) + beta * Stype(at(coord)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills with random data
|
||||
template <typename Gen>
|
||||
void fill_random(Gen generator) {
|
||||
@@ -453,7 +393,9 @@ class HostTensorView : public TensorView<T> {
|
||||
|
||||
/// Generator to fill a tensor with the identity matrix
|
||||
struct LambdaFillIdentity {
|
||||
T operator()(Coord_t const& coord) { return (coord.at(1) == coord.at(2) ? T(1) : T(0)); }
|
||||
Storage_ operator()(TensorCoord const& coord) {
|
||||
return (coord.at(1) == coord.at(2) ? Storage_(1) : Storage_(0));
|
||||
}
|
||||
};
|
||||
|
||||
/// initializes with identity
|
||||
@@ -464,39 +406,41 @@ class HostTensorView : public TensorView<T> {
|
||||
|
||||
/// Lambda for fill_linear()
|
||||
struct LambdaFillLinear {
|
||||
Coord_t v_;
|
||||
T offset_;
|
||||
TensorCoord v_;
|
||||
Storage_ offset_;
|
||||
|
||||
LambdaFillLinear(Coord_t const& _v, T _offset) : v_(_v), offset_(_offset) {}
|
||||
LambdaFillLinear(TensorCoord const& _v, Storage_ _offset) : v_(_v), offset_(_offset) {}
|
||||
|
||||
T operator()(Coord_t const& coord) { return T(v_.template dot<int>(coord)) + offset_; }
|
||||
Storage_ operator()(TensorCoord const& coord) {
|
||||
return Storage_(v_.template dot<int>(coord)) + offset_;
|
||||
}
|
||||
};
|
||||
|
||||
/// computes elements as a linear combination of their coordinates
|
||||
void fill_linear(Coord_t v, T offset = T(0)) {
|
||||
void fill_linear(TensorCoord v, Storage_ offset = Storage_(0)) {
|
||||
LambdaFillLinear lambda(v, offset);
|
||||
elementwise_generate(lambda);
|
||||
}
|
||||
|
||||
/// computes elements as a linear combination of their coordinates
|
||||
void fill_sequential(T v = T(1), T offset = T(0)) {
|
||||
int const count = size().count();
|
||||
void fill_sequential(Storage_ v = Storage_(1), Storage_ offset = Storage_(0)) {
|
||||
int const count = this->size().count();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
data()[i] = T(i);
|
||||
this->data()[i] = Storage_(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a constant value
|
||||
struct LambdaFillValue {
|
||||
T value;
|
||||
Storage_ value;
|
||||
|
||||
LambdaFillValue(T _value) : value(_value) {}
|
||||
LambdaFillValue(Storage_ _value) : value(_value) {}
|
||||
|
||||
T operator()() { return value; }
|
||||
Storage_ operator()() { return value; }
|
||||
};
|
||||
|
||||
/// fills with a value
|
||||
void fill(T val = T(0)) {
|
||||
void fill(Storage_ val = Storage_(0)) {
|
||||
LambdaFillValue op(val);
|
||||
elementwise_stream(op);
|
||||
}
|
||||
@@ -504,13 +448,21 @@ class HostTensorView : public TensorView<T> {
|
||||
/// Conversion from Src to T
|
||||
template <typename Src>
|
||||
struct LambdaAssign {
|
||||
void operator()(T& a, Src b) const { a = T(b); }
|
||||
void operator()(Storage_& a, Src b) const { a = Storage_(b); }
|
||||
};
|
||||
|
||||
/// copies from external data source and performs type conversion
|
||||
template <typename Src>
|
||||
void fill(TensorView<Src> const& tensor) {
|
||||
LambdaAssign<Src> op;
|
||||
template <
|
||||
typename SrcType,
|
||||
typename SrcMapFunc_,
|
||||
int SrcStorageRank_,
|
||||
typename SrcIndex_,
|
||||
typename SrcLongIndex_
|
||||
>
|
||||
void fill(
|
||||
TensorView<SrcType, kRank, SrcMapFunc_, SrcStorageRank_, SrcIndex_, SrcLongIndex_> const& tensor) {
|
||||
|
||||
LambdaAssign<SrcType> op;
|
||||
elementwise_in_place(op, tensor);
|
||||
}
|
||||
|
||||
@@ -520,7 +472,7 @@ class HostTensorView : public TensorView<T> {
|
||||
|
||||
LambdaNorm() : sum(0) {}
|
||||
|
||||
void operator()(T const& element) {
|
||||
void operator()(Storage const& element) {
|
||||
double value(element);
|
||||
double conj(element); // TODO - conjugates for complex
|
||||
|
||||
@@ -540,3 +492,4 @@ class HostTensorView : public TensorView<T> {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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,112 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to perform for-each operation
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,772 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 device-side elementwise operations on TensorView. Note, the operations defined
|
||||
in this header are not specialized for any particular data layout and are therefore not
|
||||
intended to offer the best possible performance. Rather, they are intended to be generic
|
||||
reference implementations to support the CUTLASS unit tests.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard Library includes
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
// CUDA includes
|
||||
#include <cublas_v2.h>
|
||||
#include <curand_kernel.h>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "tools/util/device_memory.h"
|
||||
#include "tools/util/distribution.h"
|
||||
#include "tools/util/type_traits.h"
|
||||
#include "tools/util/host_tensor.h"
|
||||
#include "tools/util/reference/device/tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace reference {
|
||||
namespace device {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Computes a random uniform distribution
|
||||
template <typename View_>
|
||||
struct RandomUniformFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
/// Parameters structure
|
||||
struct Params {
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// RNG seed
|
||||
int64_t seed;
|
||||
|
||||
/// Distriubtion
|
||||
Distribution dist;
|
||||
|
||||
/// Default ctor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params() { }
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
View const &view,
|
||||
int64_t seed,
|
||||
Distribution dist
|
||||
): view(view), seed(seed), dist(dist) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Parameters object
|
||||
Params params;
|
||||
|
||||
/// RNG state object
|
||||
curandState_t rng_state;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Device-side initialization of RNG
|
||||
CUTLASS_DEVICE
|
||||
RandomUniformFunc(Params const ¶ms): params(params) {
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
curand_init(params.seed, gtid, 0, &rng_state);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
CUTLASS_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
double range = params.dist.uniform.max - params.dist.uniform.min;
|
||||
double rnd = curand_uniform(&rng_state);
|
||||
rnd = params.dist.uniform.min + range * rnd;
|
||||
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
T result;
|
||||
if (params.dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << params.dist.int_scale)));
|
||||
result = T(rnd / double(1 << params.dist.int_scale));
|
||||
}
|
||||
else {
|
||||
result = T(rnd);
|
||||
}
|
||||
|
||||
params.view.at(coord) = result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a random Gaussian distribution
|
||||
template <typename View_>
|
||||
struct RandomGaussianFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
/// Parameters structure
|
||||
struct Params {
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// RNG seed
|
||||
int64_t seed;
|
||||
|
||||
/// RNG distribution
|
||||
Distribution dist;
|
||||
|
||||
/// Default ctor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params() { }
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
View const &view,
|
||||
int64_t seed,
|
||||
Distribution dist
|
||||
): view(view), seed(seed), dist(dist) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Parameters object
|
||||
Params params;
|
||||
|
||||
/// RNG state object
|
||||
curandState_t rng_state;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Device-side initialization of RNG
|
||||
CUTLASS_DEVICE
|
||||
RandomGaussianFunc(Params const ¶ms): params(params) {
|
||||
|
||||
uint64_t gtid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
curand_init(params.seed, gtid, 0, &rng_state);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
CUTLASS_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
double rnd = curand_normal(&rng_state);
|
||||
rnd = params.dist.gaussian.mean + params.dist.gaussian.stddev * rnd;
|
||||
|
||||
T result;
|
||||
if (params.dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << params.dist.int_scale)));
|
||||
result = T(rnd / double(1 << params.dist.int_scale));
|
||||
}
|
||||
else {
|
||||
result = T(rnd);
|
||||
}
|
||||
|
||||
params.view.at(coord) = result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a linear combination of each element
|
||||
template <typename View_>
|
||||
struct LinearCombinationFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// TensorView object
|
||||
View view;
|
||||
|
||||
/// Delta
|
||||
Coord<View::kRank, double> delta;
|
||||
|
||||
/// Offset
|
||||
double offset;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
LinearCombinationFunc(
|
||||
View const &view,
|
||||
Distribution dist
|
||||
): view(view) {
|
||||
|
||||
offset = dist.linear.offset;
|
||||
if (View::kRank >= 1) {
|
||||
delta[View::kRank - 1] = dist.linear.delta_column;
|
||||
}
|
||||
if (View::kRank >= 2) {
|
||||
delta[View::kRank - 2] = dist.linear.delta_row;
|
||||
}
|
||||
// Additional ranks have delta of zero
|
||||
for (int i = View::kRank - 2; i > 0; --i) {
|
||||
delta[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute linear combination
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
double result = offset;
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < View::kRank; ++i) {
|
||||
result += delta[i] * double(coord[i]);
|
||||
}
|
||||
view.at(coord) = T(result);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns 1 or 0 if the coordinate is along the tensor's diagonal
|
||||
template <typename View_>
|
||||
struct IdentityFunc {
|
||||
|
||||
/// TensorView
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// Default ctor
|
||||
CUTLASS_HOST_DEVICE
|
||||
IdentityFunc(View const &view): view(view) { }
|
||||
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
bool equal = true;
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < View::kRank; ++i) {
|
||||
if (coord[i] != coord[0]) {
|
||||
equal = false;
|
||||
}
|
||||
}
|
||||
view.at(coord) = equal ? T(1) : T(0);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Initializes a tensor randomly or procedurally.
|
||||
template <typename View>
|
||||
void TensorInitialize(View const &view,
|
||||
int64_t seed,
|
||||
Distribution const &dist) {
|
||||
|
||||
typedef typename View::Storage Scalar;
|
||||
|
||||
switch (dist.kind) {
|
||||
case Distribution::Uniform:
|
||||
{
|
||||
typedef detail::RandomUniformFunc<View> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
TensorForEach<Func, View::kRank, Params>(
|
||||
view.size(),
|
||||
Params(view, seed, dist)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Distribution::Gaussian:
|
||||
{
|
||||
typedef detail::RandomGaussianFunc<View> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
TensorForEach<Func, View::kRank, Params>(
|
||||
view.size(),
|
||||
Params(view, seed, dist)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Distribution::Linear:
|
||||
{
|
||||
typedef detail::LinearCombinationFunc<View> Func;
|
||||
TensorForEach<Func, View::kRank, Func>(
|
||||
view.size(),
|
||||
Func(view, dist));
|
||||
}
|
||||
break;
|
||||
case Distribution::Identity:
|
||||
{
|
||||
typedef detail::IdentityFunc<View> Func;
|
||||
|
||||
Func func(view);
|
||||
|
||||
TensorForEach<Func, View::kRank, Func>(view.size(), func);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Dispatcher to appropriate initialization kernel - preserved for backwards compatibility
|
||||
template <typename T>
|
||||
inline void tensor_initialize(Distribution const &dist,
|
||||
int64_t seed,
|
||||
int dim_contiguous,
|
||||
int dim_strided,
|
||||
T *tensor,
|
||||
int ldm) {
|
||||
|
||||
TensorView<T, 2> view(tensor, make_Coord(ldm, 1), make_Coord(dim_strided, dim_contiguous));
|
||||
reference::device::TensorInitialize(view, seed, dist);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace reference {
|
||||
namespace device {
|
||||
namespace detail {
|
||||
|
||||
/// Compares two tensor views of equal rank and dimension.
|
||||
template <typename ViewL, typename ViewR>
|
||||
struct TensorEqualsFunc {
|
||||
|
||||
/// Storage type
|
||||
typedef typename ViewL::Storage T;
|
||||
|
||||
/// Unsigned integer type of same size as View type
|
||||
typedef typename cutlass::TypeTraits<T>::unsigned_type UnsignedType;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
/// Assertions
|
||||
static_assert(ViewL::kRank == ViewR::kRank,
|
||||
"Cannot compare tensors of different rank");
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// View of right-hand-side tensor
|
||||
ViewR rhs;
|
||||
|
||||
/// Pointer to result scalar - only written with 0 if values are incorrect
|
||||
int *result;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
TensorEqualsFunc(ViewL const &lhs, ViewR const &rhs, int *result): lhs(lhs), rhs(rhs), result(result) { }
|
||||
|
||||
/// Equality check
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
UnsignedType _lhs = reinterpret_cast<UnsignedType const &>(lhs.at(coord));
|
||||
UnsignedType _rhs = reinterpret_cast<UnsignedType const &>(rhs.at(coord));
|
||||
if (_lhs != _rhs) {
|
||||
*result = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if two tensor views are equal.
|
||||
template <typename ViewL, typename ViewR>
|
||||
bool TensorEquals(ViewL const &lhs, ViewR const &rhs) {
|
||||
|
||||
// Sizes must be identical
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate device memory to contain result of kernel reduction
|
||||
HostTensor<int, 1> result(1);
|
||||
result.fill(1);
|
||||
result.sync_device();
|
||||
|
||||
typedef detail::TensorEqualsFunc<ViewL, ViewR> Func;
|
||||
Func func(lhs, rhs, result.device_data());
|
||||
|
||||
TensorForEach<Func, ViewL::kRank, Func>(lhs.size(), func);
|
||||
result.sync_host();
|
||||
|
||||
return result.at(0) != 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to apply a binary operator in place
|
||||
template <typename ViewL, typename ViewR, typename BinaryFunc>
|
||||
struct TensorFuncBinaryOp {
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// View of right-hand-side tensor
|
||||
ViewR rhs;
|
||||
|
||||
/// Binary function applied to each element
|
||||
BinaryFunc func;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
TensorFuncBinaryOp(
|
||||
ViewL const &lhs,
|
||||
ViewR const &rhs,
|
||||
BinaryFunc func = BinaryFunc()): lhs(lhs), rhs(rhs), func(func) { }
|
||||
|
||||
/// Equality check
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
lhs.at(coord) = func(lhs.at(coord), rhs.at(coord));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Helper to apply a binary operator in place
|
||||
template <typename ViewL, typename ViewR>
|
||||
struct TensorFillFunc {
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
/// Destination element type
|
||||
typedef typename ViewL::Storage DestType;
|
||||
|
||||
/// Source element type
|
||||
typedef typename ViewR::Storage SrcType;
|
||||
|
||||
/// Parameters object
|
||||
struct Params {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// View of right-hand-side tensor
|
||||
ViewR rhs;
|
||||
|
||||
/// Source offset coordinate
|
||||
TensorCoord source_offset;
|
||||
|
||||
/// Size of the subtensor copied from the source
|
||||
TensorCoord source_size;
|
||||
|
||||
/// Offset in destination
|
||||
TensorCoord dest_offset;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructs a parameters object for filling a tensor
|
||||
Params(
|
||||
ViewL const &lhs,
|
||||
ViewR const &rhs,
|
||||
TensorCoord const &source_offset = TensorCoord()
|
||||
):
|
||||
lhs(lhs), rhs(rhs), source_offset(source_offset), source_size(rhs.size() - source_offset) { }
|
||||
|
||||
/// Constructs a parameters object for filling a tensor
|
||||
Params(
|
||||
ViewL const &lhs,
|
||||
ViewR const &rhs,
|
||||
TensorCoord const &source_offset,
|
||||
TensorCoord const &source_size,
|
||||
TensorCoord const &dest_offset = TensorCoord()
|
||||
):
|
||||
lhs(lhs), rhs(rhs), source_offset(source_offset), source_size(source_size), dest_offset(dest_offset) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
Params params;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
TensorFillFunc(
|
||||
Params const ¶ms): params(params) { }
|
||||
|
||||
/// Equality check
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
TensorCoord dst_coord = params.dest_offset + coord;
|
||||
TensorCoord src_coord = params.source_offset + coord;
|
||||
|
||||
if (dst_coord < params.lhs.size() && src_coord < params.rhs.size()) {
|
||||
params.lhs.at(dst_coord) = DestType(params.rhs.at(src_coord));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Fills a TensorView with the elements from another TensorView
|
||||
template <typename ViewL, typename ViewR>
|
||||
void TensorFill(
|
||||
ViewL lhs,
|
||||
ViewR rhs,
|
||||
typename ViewL::TensorCoord const &source_offset,
|
||||
typename ViewL::TensorCoord const &source_size,
|
||||
typename ViewL::TensorCoord const &dest_offset) {
|
||||
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
TensorCoord dst_size = lhs.size() - dest_offset;
|
||||
TensorCoord src_size = rhs.size() - source_offset;
|
||||
|
||||
TensorCoord fill_size = dst_size.clamp(src_size);
|
||||
|
||||
// Fill function
|
||||
typedef detail::TensorFillFunc<ViewL, ViewR> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
Params params(lhs, rhs, source_offset, source_size, dest_offset);
|
||||
|
||||
TensorForEach<Func, ViewL::kRank, Params>(fill_size, params);
|
||||
}
|
||||
|
||||
/// Fills a TensorView with the elements from another TensorView
|
||||
template <typename ViewL, typename ViewR>
|
||||
void TensorFill(
|
||||
ViewL lhs,
|
||||
ViewR rhs,
|
||||
typename ViewL::TensorCoord const &source_offset = typename ViewL::TensorCoord()) {
|
||||
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
TensorFill(lhs, rhs, source_offset, rhs.size(), TensorCoord());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Helper to apply a binary operator in place
|
||||
template <typename ViewL>
|
||||
struct TensorFillElementFunc {
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
/// Destination element type
|
||||
typedef typename ViewL::Storage DestType;
|
||||
|
||||
/// Parameters object
|
||||
struct Params {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// Source offset coordinate
|
||||
TensorCoord offset;
|
||||
|
||||
/// Element to overwrite with
|
||||
DestType value;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructs a parameters object for filling a tensor
|
||||
CUTLASS_HOST_DEVICE
|
||||
Params(
|
||||
ViewL const &lhs,
|
||||
DestType const &value,
|
||||
TensorCoord const &offset = TensorCoord()
|
||||
):
|
||||
lhs(lhs), value(value), offset(offset) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
Params params;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
CUTLASS_HOST_DEVICE
|
||||
TensorFillElementFunc(
|
||||
Params const ¶ms): params(params) { }
|
||||
|
||||
/// Equality check
|
||||
CUTLASS_HOST_DEVICE
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
TensorCoord dst_coord = params.offset + coord;
|
||||
|
||||
if (dst_coord < params.size) {
|
||||
params.lhs.at(dst_coord) = params.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Method to perform the actual fill
|
||||
template <typename ViewL>
|
||||
void TensorFillElement(
|
||||
ViewL const &lhs,
|
||||
typename ViewL::Storage const &value,
|
||||
typename ViewL::TensorCoord const &offset,
|
||||
typename ViewL::TensorCoord const &size) {
|
||||
|
||||
// Fill function
|
||||
typedef detail::TensorFillElementFunc<ViewL> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
Params params(lhs, value, offset);
|
||||
|
||||
TensorForEach<Func, ViewL::kRank, Params>(size, params);
|
||||
}
|
||||
|
||||
/// Fills a tensor
|
||||
template <typename ViewL>
|
||||
void TensorFillElement(
|
||||
ViewL lhs,
|
||||
typename ViewL::Storage value,
|
||||
typename ViewL::TensorCoord const &offset =typename ViewL::Storage()) {
|
||||
|
||||
TensorFillElement(lhs, value, offset, lhs.size() - offset);
|
||||
}
|
||||
|
||||
/// Constructs a parameters object for filling a tensor
|
||||
template <typename ViewL>
|
||||
void TensorFillElement(
|
||||
ViewL lhs,
|
||||
typename ViewL::Storage value,
|
||||
typename ViewL::Storage const &offset,
|
||||
typename ViewL::Storage const &size) {
|
||||
|
||||
TensorFillElement(lhs, value, offset, size);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 "tools/util/reference/device/kernel/tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace device {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Launches a kernel 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);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace device
|
||||
} // namespace reference
|
||||
} // namesace cutlass
|
||||
@@ -0,0 +1,270 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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_coord.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Template function to compute an inner product.
|
||||
template <typename Atype, typename Btype, typename Ctype>
|
||||
Ctype inner_product(Atype a, Btype b, Ctype c) {
|
||||
return Ctype(a) * Ctype(b) + c;
|
||||
}
|
||||
|
||||
/// Specialization for matrix multiplication with binary operands
|
||||
template <>
|
||||
inline int inner_product<Vector<bin1_t, 32>, Vector<bin1_t, 32>, int>(
|
||||
Vector<bin1_t, 32> a,
|
||||
Vector<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 <> inline
|
||||
int inner_product<Vector<int4_t, 8>, Vector<int4_t, 8>, int>(
|
||||
Vector<int4_t, 8> a,
|
||||
Vector<int4_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 <> inline
|
||||
int inner_product<Vector<uint4_t, 8>, Vector<uint4_t, 8>, int>(
|
||||
Vector<uint4_t, 8> a,
|
||||
Vector<uint4_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
|
||||
static inline DstType apply(SrcType src) { return static_cast<DstType>(src); };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Cast<float, int8_t> {
|
||||
static inline 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> {
|
||||
static inline 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// 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 TensorRefA,
|
||||
typename TensorRefB,
|
||||
typename TensorRefC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType
|
||||
>
|
||||
void Gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRefA tensor_a,
|
||||
TensorRefB tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefC tensor_c,
|
||||
AccumulatorType initial_accum) {
|
||||
|
||||
typedef typename TensorRefA::Storage AType;
|
||||
typedef typename TensorRefB::Storage BType;
|
||||
typedef typename TensorRefC::Storage CType;
|
||||
|
||||
static_assert(
|
||||
TensorRefA::kRank == 2 &&
|
||||
TensorRefB::kRank == 2 &&
|
||||
TensorRefC::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 = 32;
|
||||
int const Nblock = 32;
|
||||
|
||||
for (int row_block = 0; row_block < M; row_block += Mblock) {
|
||||
for (int col_block = 0; col_block < N; col_block += Nblock) {
|
||||
AccumulatorType 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) {
|
||||
AType a = tensor_a.at(MatrixCoord(row, k_block));
|
||||
BType b = tensor_b.at(MatrixCoord(k_block, col));
|
||||
|
||||
accum[i][j] = detail::inner_product(a, 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_c.at(coord) = detail::Cast<ScalarType, CType>::apply(
|
||||
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 TensorRefA,
|
||||
typename TensorRefB,
|
||||
typename TensorRefC,
|
||||
typename ScalarType
|
||||
>
|
||||
void Gemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRefA tensor_a,
|
||||
TensorRefB tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefC tensor_c) {
|
||||
|
||||
Gemm(problem_size, alpha, tensor_a, tensor_b, beta, tensor_c, ScalarType(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Computes a batch of GEMMs over a set of matrices of common dimension.
|
||||
template <
|
||||
typename TensorRefCollectionA,
|
||||
typename TensorRefCollectionB,
|
||||
typename TensorRefCollectionC,
|
||||
typename ScalarType,
|
||||
typename AccumulatorType
|
||||
>
|
||||
void BatchGemm(
|
||||
gemm::GemmCoord problem_size,
|
||||
ScalarType alpha,
|
||||
TensorRefCollectionA const& tensor_a,
|
||||
TensorRefCollectionB const& tensor_b,
|
||||
ScalarType beta,
|
||||
TensorRefCollectionC &tensor_c,
|
||||
AccumulatorType initial_accum = AccumulatorType(0)) {
|
||||
|
||||
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 < problem_size.batch();
|
||||
++batch, ++tensor_a_it, ++tensor_b_it, ++tensor_c_it) {
|
||||
|
||||
Gemm(
|
||||
problem_size,
|
||||
alpha,
|
||||
*tensor_a_it,
|
||||
*tensor_b_it,
|
||||
beta,
|
||||
*tensor_c_it,
|
||||
initial_accum);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,478 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 <fstream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
// Cutlass includes
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "tools/util/distribution.h"
|
||||
#include "tools/util/type_traits.h"
|
||||
#include "tools/util/reference/host/tensor_foreach.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace reference {
|
||||
namespace host {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Computes a random uniform distribution
|
||||
template <typename View_>
|
||||
struct RandomUniformFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
/// Parameters structure
|
||||
struct Params {
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// RNG seed
|
||||
unsigned seed;
|
||||
|
||||
/// Distriubtion
|
||||
Distribution dist;
|
||||
|
||||
/// Default ctor
|
||||
Params() { }
|
||||
|
||||
/// Constructor
|
||||
Params(
|
||||
View const &view,
|
||||
unsigned seed,
|
||||
Distribution dist
|
||||
): view(view), seed(seed), dist(dist) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Parameters object
|
||||
Params params;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Device-side initialization of RNG
|
||||
RandomUniformFunc(Params const ¶ms): params(params) {
|
||||
std::srand(params.seed);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
double range = params.dist.uniform.max - params.dist.uniform.min;
|
||||
|
||||
double rnd = double(std::rand()) / double(RAND_MAX);
|
||||
|
||||
rnd = params.dist.uniform.min + range * rnd;
|
||||
|
||||
// Random values are cast to integer after scaling by a power of two to facilitate error
|
||||
// testing
|
||||
T result;
|
||||
if (params.dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << params.dist.int_scale)));
|
||||
result = T(rnd / double(1 << params.dist.int_scale));
|
||||
}
|
||||
else {
|
||||
result = T(rnd);
|
||||
}
|
||||
|
||||
params.view.at(coord) = result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a random Gaussian distribution
|
||||
template <typename View_>
|
||||
struct RandomGaussianFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
/// Parameters structure
|
||||
struct Params {
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// RNG seed
|
||||
unsigned seed;
|
||||
|
||||
/// RNG distribution
|
||||
Distribution dist;
|
||||
|
||||
/// Default ctor
|
||||
Params() { }
|
||||
|
||||
/// Constructor
|
||||
Params(
|
||||
View const &view,
|
||||
unsigned seed,
|
||||
Distribution dist
|
||||
): view(view), seed(seed), dist(dist) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Parameters object
|
||||
Params params;
|
||||
|
||||
/// Constant PI
|
||||
double pi;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Device-side initialization of RNG
|
||||
RandomGaussianFunc(Params const ¶ms): params(params) {
|
||||
pi = std::acos(-1);
|
||||
}
|
||||
|
||||
/// Compute random value and update RNG state
|
||||
void operator()(TensorCoord const &coord) {
|
||||
|
||||
// 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);
|
||||
|
||||
double rnd = std::sqrt(-2 * std::log(u1)) * std::cos(2 * pi * u2);
|
||||
|
||||
// Scale according to Gaussian distribution parameters
|
||||
rnd = params.dist.gaussian.mean + params.dist.gaussian.stddev * rnd;
|
||||
|
||||
T result;
|
||||
if (params.dist.int_scale >= 0) {
|
||||
rnd = double(int(rnd * double(1 << params.dist.int_scale)));
|
||||
result = T(rnd / double(1 << params.dist.int_scale));
|
||||
}
|
||||
else {
|
||||
result = T(rnd);
|
||||
}
|
||||
|
||||
params.view.at(coord) = result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Computes a linear combination of each element
|
||||
template <typename View_>
|
||||
struct LinearCombinationFunc {
|
||||
|
||||
/// View type
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// TensorView object
|
||||
View view;
|
||||
|
||||
/// Delta
|
||||
Coord<View::kRank, double> delta;
|
||||
|
||||
/// Offset
|
||||
double offset;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
LinearCombinationFunc(
|
||||
View const &view,
|
||||
Distribution dist
|
||||
): view(view) {
|
||||
|
||||
offset = dist.linear.offset;
|
||||
if (View::kRank >= 1) {
|
||||
delta[View::kRank - 1] = dist.linear.delta_column;
|
||||
}
|
||||
if (View::kRank >= 2) {
|
||||
delta[View::kRank - 2] = dist.linear.delta_row;
|
||||
}
|
||||
// Additional ranks have delta of zero
|
||||
for (int i = View::kRank - 2; i > 0; --i) {
|
||||
delta[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute linear combination
|
||||
void operator()(TensorCoord const &coord) {
|
||||
double result = offset;
|
||||
|
||||
for (int i = 0; i < View::kRank; ++i) {
|
||||
result += delta[i] * double(coord[i]);
|
||||
}
|
||||
view.at(coord) = T(result);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns 1 or 0 if the coordinate is along the tensor's diagonal
|
||||
template <typename View_>
|
||||
struct IdentityFunc {
|
||||
|
||||
/// TensorView
|
||||
typedef View_ View;
|
||||
|
||||
/// Scalar type
|
||||
typedef typename View::Storage T;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename View::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View object
|
||||
View view;
|
||||
|
||||
/// Default ctor
|
||||
IdentityFunc(View const &view): view(view) { }
|
||||
|
||||
/// Computes an identity
|
||||
void operator()(TensorCoord const &coord) {
|
||||
bool equal = true;
|
||||
for (int i = 0; i < View::kRank; ++i) {
|
||||
if (coord[i] != coord[0]) {
|
||||
equal = false;
|
||||
}
|
||||
}
|
||||
view.at(coord) = equal ? T(1) : T(0);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Initializes a tensor randomly or procedurally.
|
||||
template <typename View>
|
||||
void TensorInitialize(View const &view,
|
||||
unsigned seed,
|
||||
Distribution const &dist) {
|
||||
|
||||
typedef typename View::Storage Scalar;
|
||||
|
||||
switch (dist.kind) {
|
||||
case Distribution::Uniform:
|
||||
{
|
||||
typedef detail::RandomUniformFunc<View> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
TensorForEach<Func, View::kRank, Params>(
|
||||
view.size(),
|
||||
Params(view, seed, dist)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Distribution::Gaussian:
|
||||
{
|
||||
typedef detail::RandomGaussianFunc<View> Func;
|
||||
typedef typename Func::Params Params;
|
||||
|
||||
TensorForEach<Func, View::kRank, Params>(
|
||||
view.size(),
|
||||
Params(view, seed, dist)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Distribution::Linear:
|
||||
{
|
||||
typedef detail::LinearCombinationFunc<View> Func;
|
||||
TensorForEach<Func, View::kRank, Func>(
|
||||
view.size(),
|
||||
Func(view, dist));
|
||||
}
|
||||
break;
|
||||
case Distribution::Identity:
|
||||
{
|
||||
typedef detail::IdentityFunc<View> Func;
|
||||
|
||||
Func func(view);
|
||||
|
||||
TensorForEach<Func, View::kRank, Func>(view.size(), func);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Compares two tensor views of equal rank and dimension.
|
||||
template <typename ViewL, typename ViewR>
|
||||
struct TensorEqualsFunc {
|
||||
|
||||
/// Storage type
|
||||
typedef typename ViewL::Storage T;
|
||||
|
||||
/// Unsigned integer type of same size as View type
|
||||
typedef typename cutlass::TypeTraits<T>::unsigned_type UnsignedType;
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
/// Assertions
|
||||
static_assert(ViewL::kRank == ViewR::kRank,
|
||||
"Cannot compare tensors of different rank");
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// View of right-hand-side tensor
|
||||
ViewR rhs;
|
||||
|
||||
/// Pointer to result scalar - only written with 0 if values are incorrect
|
||||
int *result;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
TensorEqualsFunc(ViewL const &lhs, ViewR const &rhs, int *result): lhs(lhs), rhs(rhs), result(result) { }
|
||||
|
||||
/// Equality check
|
||||
void operator()(TensorCoord const &coord) {
|
||||
UnsignedType _lhs = reinterpret_cast<UnsignedType const &>(lhs.at(coord));
|
||||
UnsignedType _rhs = reinterpret_cast<UnsignedType const &>(rhs.at(coord));
|
||||
if (_lhs != _rhs) {
|
||||
*result = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if two tensor views are equal.
|
||||
template <typename ViewL, typename ViewR>
|
||||
bool TensorEquals(ViewL const &lhs, ViewR const &rhs) {
|
||||
|
||||
// Sizes must be identical
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = 1;
|
||||
|
||||
typedef detail::TensorEqualsFunc<ViewL, ViewR> Func;
|
||||
Func func(lhs, rhs, &result);
|
||||
|
||||
TensorForEach<Func, ViewL::kRank, Func>(lhs.size(), func);
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Helper to apply a binary operator in place
|
||||
template <typename ViewL, typename ViewR, typename BinaryFunc>
|
||||
struct TensorFuncBinaryOp {
|
||||
|
||||
/// Coordinate in tensor's index space
|
||||
typedef typename ViewL::TensorCoord TensorCoord;
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// View of left-hand-side tensor
|
||||
ViewL lhs;
|
||||
|
||||
/// View of right-hand-side tensor
|
||||
ViewR rhs;
|
||||
|
||||
/// Binary function applied to each element
|
||||
BinaryFunc func;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
TensorFuncBinaryOp(
|
||||
ViewL const &lhs,
|
||||
ViewR const &rhs,
|
||||
BinaryFunc func = BinaryFunc()): lhs(lhs), rhs(rhs), func(func) { }
|
||||
|
||||
/// Equality check
|
||||
void operator()(TensorCoord const &coord) {
|
||||
lhs.at(coord) = func(lhs.at(coord), rhs.at(coord));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,102 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2018, 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 "tools/util/reference/device/kernel/tensor_foreach.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 &size,
|
||||
Coord<Rank> &coord) {
|
||||
|
||||
for (int i = 0; i < size.at(kActiveRank); ++i) {
|
||||
coord[kActiveRank] = i;
|
||||
TensorForEachHelper<Func, Rank, RankRemaining - 1>(func, size, 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 &size,
|
||||
Coord<Rank> &coord) {
|
||||
|
||||
for (int i = 0; i < size.at(kActiveRank); ++i) {
|
||||
coord[kActiveRank] = i;
|
||||
func(coord);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Iterates over the index space of a tensor
|
||||
template <typename Func, int Rank, typename Params>
|
||||
struct TensorForEach {
|
||||
|
||||
/// Constructor performs the operation.
|
||||
TensorForEach(Coord<Rank> size, Params params = Params()) {
|
||||
|
||||
Func func(params);
|
||||
Coord<Rank> coord;
|
||||
|
||||
detail::TensorForEachHelper<Func, Rank, Rank - 1>(func, size, coord);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace host
|
||||
} // namespace reference
|
||||
} // namespace cutlass
|
||||
+124
-27
@@ -24,38 +24,135 @@
|
||||
**************************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cutlass/core_io.h>
|
||||
#include <cutlass/tensor_view.h>
|
||||
#include "cutlass/core_io.h"
|
||||
#include "cutlass/tensor_view.h"
|
||||
|
||||
template <typename T>
|
||||
inline std::ostream& tensor_view_output(std::ostream& out, T t) {
|
||||
out << t;
|
||||
return out;
|
||||
}
|
||||
namespace cutlass {
|
||||
|
||||
template <>
|
||||
inline std::ostream& tensor_view_output<int8_t>(std::ostream& out, int8_t t) {
|
||||
out << int(t);
|
||||
return out;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
inline std::ostream& operator<<(std::ostream& out, cutlass::TensorView<T> const& tensor) {
|
||||
for (int batch = 0; batch < tensor.size(0); ++batch) {
|
||||
out << "[\n ";
|
||||
for (int h = 0; h < tensor.size(1); ++h) {
|
||||
for (int w = 0; w < tensor.size(2); ++w) {
|
||||
for (int c = 0; c < tensor.size(3); ++c) {
|
||||
out << ((c | w) ? ", " : "");
|
||||
tensor_view_output(out, tensor.at(cutlass::make_Coord(batch, h, w, c)));
|
||||
}
|
||||
}
|
||||
if (h + 1 < tensor.size(1)) {
|
||||
out << " ;\n ";
|
||||
}
|
||||
namespace detail {
|
||||
|
||||
/// Helper to write the least significant rank of a TensorView
|
||||
template <
|
||||
typename Storage_,
|
||||
int Rank_,
|
||||
typename MapFunc_,
|
||||
int StorageRank_,
|
||||
typename Index_,
|
||||
typename LongIndex_
|
||||
>
|
||||
inline std::ostream & TensorView_WriteLeastSignificantRank(
|
||||
std::ostream& out,
|
||||
cutlass::TensorView<
|
||||
Storage_,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> const& tensor,
|
||||
cutlass::Coord<Rank_> const &start_coord,
|
||||
int rank,
|
||||
std::streamsize width) {
|
||||
|
||||
for (int idx = 0; idx < tensor.size(rank); ++idx) {
|
||||
|
||||
Coord<Rank_> coord(start_coord);
|
||||
coord[rank] = idx;
|
||||
|
||||
if (idx) {
|
||||
out.width(0);
|
||||
out << ", ";
|
||||
}
|
||||
out << " ]";
|
||||
if (idx || coord) {
|
||||
out.width(width);
|
||||
}
|
||||
out << ScalarIO<Storage_>(tensor.at(coord));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Helper to write a rank of a TensorView
|
||||
template <
|
||||
typename Storage_,
|
||||
int Rank_,
|
||||
typename MapFunc_,
|
||||
int StorageRank_,
|
||||
typename Index_,
|
||||
typename LongIndex_
|
||||
>
|
||||
inline std::ostream & TensorView_WriteRank(
|
||||
std::ostream& out,
|
||||
cutlass::TensorView<
|
||||
Storage_,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> const& tensor,
|
||||
cutlass::Coord<Rank_> const &start_coord,
|
||||
int rank,
|
||||
std::streamsize width) {
|
||||
|
||||
// If called on the least significant rank, write the result as a row
|
||||
if (rank + 1 == Rank_) {
|
||||
return TensorView_WriteLeastSignificantRank(out, tensor, start_coord, rank, width);
|
||||
}
|
||||
|
||||
// Otherwise, write a sequence of rows and newlines
|
||||
for (int idx = 0; idx < tensor.size(rank); ++idx) {
|
||||
|
||||
Coord<Rank_> coord(start_coord);
|
||||
coord[rank] = idx;
|
||||
|
||||
if (rank + 2 == Rank_) {
|
||||
// Write least significant ranks asa matrix with rows delimited by ";\n"
|
||||
out << (idx ? ";\n" : "");
|
||||
TensorView_WriteLeastSignificantRank(out, tensor, coord, rank + 1, width);
|
||||
}
|
||||
else {
|
||||
// Higher ranks are separated by newlines
|
||||
out << (idx ? "\n" : "");
|
||||
TensorView_WriteRank(out, tensor, coord, rank + 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Prints human-readable representation of a TensorView to an ostream
|
||||
template <
|
||||
typename Storage_,
|
||||
int Rank_,
|
||||
typename MapFunc_,
|
||||
int StorageRank_,
|
||||
typename Index_,
|
||||
typename LongIndex_
|
||||
>
|
||||
inline std::ostream& operator<<(
|
||||
std::ostream& out,
|
||||
TensorView<
|
||||
Storage_,
|
||||
Rank_,
|
||||
MapFunc_,
|
||||
StorageRank_,
|
||||
Index_,
|
||||
LongIndex_> const& tensor) {
|
||||
|
||||
// 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, tensor, Coord<Rank_>(), 0, out.width());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
|
||||
+110
-1
@@ -33,12 +33,52 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "half.h"
|
||||
#include "cutlass/vector.h"
|
||||
#include "cutlass/util/complex.h"
|
||||
|
||||
namespace cutlass {
|
||||
struct half_t;
|
||||
|
||||
template <typename T>
|
||||
struct TypeTraits;
|
||||
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; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<Vector<bin1_t, 32> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32I;
|
||||
typedef Vector<bin1_t, 32> host_type;
|
||||
typedef Vector<bin1_t, 32> 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; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits< Vector<int4_t, 8> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32I;
|
||||
typedef Vector<int4_t, 8> host_type;
|
||||
typedef Vector<int4_t, 8> 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; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits< Vector<uint4_t, 8> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_R_32I;
|
||||
typedef Vector<uint4_t, 8> host_type;
|
||||
typedef Vector<uint4_t, 8> 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; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<int8_t> {
|
||||
@@ -158,4 +198,73 @@ struct TypeTraits<double> {
|
||||
static inline double remove_negative_zero(double x) { return x == -0.0 ? 0.0 : x; }
|
||||
static inline double to_print(double x) { return x; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Complex types
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <>
|
||||
struct TypeTraits<platform::complex<half> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_16F;
|
||||
typedef platform::complex<half_t> host_type;
|
||||
typedef platform::complex<half> device_type;
|
||||
typedef int16_t integer_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<platform::complex<half_t> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_16F;
|
||||
typedef platform::complex<half_t> host_type;
|
||||
typedef platform::complex<half> device_type;
|
||||
typedef int16_t integer_type;
|
||||
typedef uint16_t unsigned_type;
|
||||
static inline platform::complex<half_t> remove_negative_zero(platform::complex<half_t> x) {
|
||||
return platform::complex<half_t>(
|
||||
real(x) == -0.f ? half_t(0) : real(x),
|
||||
imag(x) == -0.f ? half_t(0) : imag(x)
|
||||
);
|
||||
}
|
||||
static inline platform::complex<half_t> to_print(platform::complex<half_t> x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<platform::complex<float> > {
|
||||
|
||||
static cudaDataType_t const cublas_type = CUDA_C_32F;
|
||||
typedef platform::complex<float> host_type;
|
||||
typedef platform::complex<float> device_type;
|
||||
typedef int64_t integer_type;
|
||||
typedef uint64_t unsigned_type;
|
||||
|
||||
static inline platform::complex<float> remove_negative_zero(platform::complex<float> x) {
|
||||
return platform::complex<float>(
|
||||
real(x) == -0.f ? 0.f : real(x),
|
||||
imag(x) == -0.f ? 0.f : imag(x)
|
||||
);
|
||||
}
|
||||
|
||||
static inline platform::complex<float> to_print(platform::complex<float> x) { return x; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeTraits<platform::complex<double> > {
|
||||
static cudaDataType_t const cublas_type = CUDA_C_64F;
|
||||
typedef platform::complex<double> host_type;
|
||||
typedef platform::complex<double> device_type;
|
||||
struct integer_type { int64_t real, imag; };
|
||||
struct unsigned_type { uint64_t real, imag; };
|
||||
static inline platform::complex<double> remove_negative_zero(platform::complex<double> x) {
|
||||
return platform::complex<double>(
|
||||
real(x) == -0.0 ? 0.0 : real(x),
|
||||
imag(x) == -0.0 ? 0.0 : imag(x)
|
||||
);
|
||||
}
|
||||
static inline platform::complex<double> to_print(platform::complex<double> x) { return x; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace cutlass
|
||||
|
||||
Reference in New Issue
Block a user