Committing CUTLASS for release.

This commit is contained in:
akerr
2017-12-04 21:12:52 -08:00
parent bbb3178126
commit d08ba8ac46
35 changed files with 10786 additions and 29 deletions
+312
View File
@@ -0,0 +1,312 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* Utility for parsing command line arguments
*/
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <limits>
#include <cuda_runtime.h>
#include <cutlass/util/debug.h>
namespace cutlass {
/******************************************************************************
* command_line
******************************************************************************/
/**
* Utility for parsing command line arguments
*/
struct command_line
{
std::vector<std::string> keys;
std::vector<std::string> values;
std::vector<std::string> args;
int device_id;
cudaDeviceProp device_prop;
float device_giga_bandwidth;
size_t device_free_physmem;
size_t device_total_physmem;
/**
* Constructor
*/
command_line(int argc, const char **argv, int device_id = -1) :
keys(10),
values(10),
device_id(device_id)
{
using namespace std;
for (int i = 1; i < argc; i++)
{
string arg = argv[i];
if ((arg[0] != '-') || (arg[1] != '-'))
{
args.push_back(arg);
continue;
}
string::size_type pos;
string key, val;
if ((pos = arg.find('=')) == string::npos) {
key = string(arg, 2, arg.length() - 2);
val = "";
} else {
key = string(arg, 2, pos - 2);
val = string(arg, pos + 1, arg.length() - 1);
}
keys.push_back(key);
values.push_back(val);
}
// Initialize device
CUDA_PERROR_EXIT(device_init());
}
/**
* Checks whether a flag "--<flag>" is present in the commandline
*/
bool check_cmd_line_flag(const char* arg_name)
{
using namespace std;
for (int i = 0; i < int(keys.size()); ++i)
{
if (keys[i] == string(arg_name))
return true;
}
return false;
}
/**
* Returns number of naked (non-flag and non-key-value) commandline parameters
*/
template <typename value_t>
int num_naked_args()
{
return args.size();
}
/**
* Returns the commandline parameter for a given index (not including flags)
*/
template <typename value_t>
void get_cmd_line_argument(int index, value_t &val)
{
using namespace std;
if (index < args.size()) {
istringstream str_stream(args[index]);
str_stream >> val;
}
}
/**
* Returns the value specified for a given commandline parameter --<flag>=<value>
*/
template <typename value_t>
void get_cmd_line_argument(const char *arg_name, value_t &val)
{
using namespace std;
for (int i = 0; i < int(keys.size()); ++i)
{
if (keys[i] == string(arg_name))
{
istringstream str_stream(values[i]);
str_stream >> val;
}
}
}
/**
* Returns the values specified for a given commandline parameter --<flag>=<value>,<value>*
*/
template <typename value_t>
void get_cmd_line_arguments(
const char *arg_name,
std::vector<value_t> &vals,
char sep = ',')
{
using namespace std;
if (check_cmd_line_flag(arg_name))
{
// Clear any default values
vals.clear();
// Recover from multi-value string
for (int i = 0; i < keys.size(); ++i)
{
if (keys[i] == string(arg_name))
{
string val_string(values[i]);
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);
}
}
}
}
/**
* The number of pairs parsed
*/
int parsed_argc()
{
return (int) keys.size();
}
/**
* Initialize device
*/
cudaError_t device_init()
{
cudaError_t error = cudaSuccess;
do
{
int deviceCount;
if (CUDA_PERROR(error = cudaGetDeviceCount(&deviceCount))) break;
if (deviceCount == 0) {
fprintf(stderr, "No devices supporting CUDA.\n");
exit(1);
}
if (device_id < 0)
{
get_cmd_line_argument("device", device_id);
}
if ((device_id > deviceCount - 1) || (device_id < 0))
{
device_id = 0;
}
if (CUDA_PERROR(error = cudaSetDevice(device_id))) break;
if (CUDA_PERROR(error = cudaMemGetInfo(&device_free_physmem, &device_total_physmem))) break;
if (CUDA_PERROR(error = cudaGetDeviceProperties(&device_prop, device_id))) break;
if (device_prop.major < 1) {
fprintf(stderr, "Device does not support CUDA.\n");
exit(1);
}
device_giga_bandwidth = float(device_prop.memoryBusWidth) * device_prop.memoryClockRate * 2 / 8 / 1000 / 1000;
} while (0);
return error;
}
//-------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------
/// Tokenizes a comma-delimited list of string pairs delimited by ':'
static void tokenize(
std::vector<std::pair<std::string, std::string> > &tokens,
std::string const &str,
char delim = ',',
char sep = ':')
{
// Home-built to avoid Boost dependency
size_t s_idx = 0;
size_t d_idx = std::string::npos;
while (s_idx < str.size())
{
d_idx = str.find_first_of(delim, s_idx);
size_t end_idx = (d_idx != std::string::npos ? d_idx : str.size());
size_t sep_idx = str.find_first_of(sep, s_idx);
size_t offset = 1;
if (sep_idx == std::string::npos || sep_idx >= end_idx)
{
sep_idx = end_idx;
offset = 0;
}
std::pair<std::string, std::string> item(
str.substr(s_idx, sep_idx - s_idx),
str.substr(sep_idx + offset, end_idx - sep_idx - offset));
tokens.push_back(item);
s_idx = end_idx + 1;
}
}
/// Tokenizes a comma-delimited list of string pairs delimited by ':'
static void tokenize(
std::vector<std::string > &tokens,
std::string const &str,
char delim = ',',
char sep = ':')
{
std::vector<std::pair<std::string, std::string> > token_pairs;
tokenize(token_pairs, str, delim, sep);
for (auto const &tok : token_pairs)
{
tokens.push_back(tok.first);
}
}
};
} // namespace cutlass
+83
View File
@@ -0,0 +1,83 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* \brief C++ exception semantics for CUDA error codes
*/
#include <iosfwd>
#include <cuda_runtime.h>
namespace cutlass {
/// C++ exception wrapper for CUDA \p cudaError_t
class cuda_exception : public std::exception
{
public:
/// Constructor
cuda_exception(
const char *msg = "",
cudaError_t err = cudaErrorUnknown)
:
msg(msg), err(err)
{}
/// Returns the explanatory string
const char *what() const noexcept
{
return msg;
}
/// Returns the underlying CUDA \p cudaError_t
cudaError_t cudaError() const
{
return err;
}
protected:
/// Explanatory string
const char *msg;
/// Underlying CUDA \p cudaError_t
cudaError_t err;
};
/// Writes a cudaError_t to an output stream
inline std::ostream & operator<<(std::ostream &out, cudaError_t result)
{
return out << cudaGetErrorString(result);
}
/// Writes a cuda_exception instance to an output stream
inline std::ostream & operator<<(std::ostream &out, cuda_exception const &e)
{
return out << e.what() << ": " << e.cudaError();
}
} // namespace cutlass
+224
View File
@@ -0,0 +1,224 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* Utilities for interacting with the opaque CUDA __half type
*/
#include <stdint.h>
#include <cuda_fp16.h>
#include <iosfwd>
namespace cutlass {
/******************************************************************************
* half_t
******************************************************************************/
/**
* Host-based fp16 data type compatible and convertible with __half
*/
struct half_t
{
uint16_t __x;
/// Constructor from __half
half_t(const __half &other)
{
__x = reinterpret_cast<const uint16_t&>(other);
}
/// Constructor from integer
half_t(int a)
{
*this = half_t(float(a));
}
/// Constructor from float
half_t(float a)
{
uint32_t ia = *reinterpret_cast<uint32_t*>(&a);
uint16_t ir;
ir = (ia >> 16) & 0x8000;
if ((ia & 0x7f800000) == 0x7f800000)
{
if ((ia & 0x7fffffff) == 0x7f800000)
{
ir |= 0x7c00; /* infinity */
}
else
{
ir = 0x7fff; /* canonical NaN */
}
}
else if ((ia & 0x7f800000) >= 0x33000000)
{
int32_t shift = (int32_t) ((ia >> 23) & 0xff) - 127;
if (shift > 15)
{
ir |= 0x7c00; /* infinity */
}
else
{
ia = (ia & 0x007fffff) | 0x00800000; /* extract mantissa */
if (shift < -14)
{ /* denormal */
ir |= ia >> (-1 - shift);
ia = ia << (32 - (-1 - shift));
}
else
{ /* normal */
ir |= ia >> (24 - 11);
ia = ia << (32 - (24 - 11));
ir = ir + ((14 + shift) << 10);
}
/* IEEE-754 round to nearest of even */
if ((ia > 0x80000000) || ((ia == 0x80000000) && (ir & 1)))
{
ir++;
}
}
}
this->__x = ir;
}
/// Cast to __half
operator __half() const
{
return reinterpret_cast<const __half&>(__x);
}
/// Cast to float
operator float() const
{
int sign = ((this->__x >> 15) & 1);
int exp = ((this->__x >> 10) & 0x1f);
int mantissa = (this->__x & 0x3ff);
uint32_t f = 0;
if (exp > 0 && exp < 31)
{
// normal
exp += 112;
f = (sign << 31) | (exp << 23) | (mantissa << 13);
}
else if (exp == 0)
{
if (mantissa)
{
// subnormal
exp += 113;
while ((mantissa & (1 << 10)) == 0)
{
mantissa <<= 1;
exp--;
}
mantissa &= 0x3ff;
f = (sign << 31) | (exp << 23) | (mantissa << 13);
}
else
{
// zero
f = 0;
}
}
else if (exp == 31)
{
if (mantissa)
{
f = 0x7fffffff; // not a number
}
else
{
f = (0xff << 23) | (sign << 31); // inf
}
}
return *reinterpret_cast<float const *>(&f);
}
/// Get raw storage
uint16_t raw()
{
return this->__x;
}
/// Assignment by sum
bool operator ==(const half_t &other)
{
return (this->__x == other.__x);
}
/// Increment
half_t& operator +=(const half_t &rhs)
{
*this = half_t(float(*this) + float(rhs));
return *this;
}
/// Decrement
half_t& operator -=(const half_t &rhs)
{
*this = half_t(float(*this) - float(rhs));
return *this;
}
/// Multiply
half_t operator*(const half_t &other)
{
return half_t(float(*this) * float(other));
}
/// Multiply
half_t operator+(const half_t &other)
{
return half_t(float(*this) + float(other));
}
};
/******************************************************************************
* I/O stream overloads
******************************************************************************/
/// Insert formatted \p half_t into the output stream
std::ostream& operator<<(std::ostream &out, const half_t &x)
{
out << (float)x;
return out;
}
/// Insert formatted \p __half into the output stream
std::ostream& operator<<(std::ostream &out, const __half &x)
{
return out << half_t(x);
}
} // namespace cutlass
+495
View File
@@ -0,0 +1,495 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* Matrix data structure providing basic CPU-based algorithms and
* operations that can be cloned and synchronized in GPU device memory
*/
#include <vector>
#include <fstream>
#include <cutlass/util/debug.h>
#include "../cutlass/util/matrix_transform.h"
#include "half.h"
namespace cutlass {
/**
* \brief Matrix data structure providing basic CPU-based algorithms and
* operations that be synchronized with a GPU-based replica
*/
template <typename value_t>
struct matrix
{
// Host value type (must be convertible to/from value_t)
typedef typename nv_std::conditional<
(nv_std::is_same<value_t, __half>::value), // If (value_t == __half) ...
half_t, // ... use half_t internally for host storage, else...
value_t>::type // ... use value_t directly
host_value_t;
//-----------------------------------------------------------------------------
// Data members
//-----------------------------------------------------------------------------
private:
/// M dimension (height in rows)
int _m;
/// N dimension (width in columns)
int _n;
/// Data array on host
std::vector<host_value_t> _h_data;
/// Clone of data array on GPU device
value_t *_d_data;
/// GPU Device identifier that clone synchronizes with
int _device_id;
public:
//-----------------------------------------------------------------------------
// Lifetime and synchronization
//-----------------------------------------------------------------------------
/**
* Constructor: zero-initializes the matrix.
*/
matrix(
int m, ///< Height of the matrix in rows
int n) ///< Width of the matrix in columns
:
_m(m),
_n(n),
_d_data(NULL),
_device_id(0)
{
_h_data.resize(_m * _n, 0);
CUDA_PERROR_EXIT(cudaMalloc((void ** )&_d_data, sizeof(value_t) * _m * _n));
CUDA_PERROR_EXIT(cudaGetDevice(&_device_id));
}
/// Destructor
~matrix()
{
if (_d_data)
{
CUDA_PERROR_EXIT(cudaFree(_d_data));
}
}
/**
* Synchronize the GPU-based replica with the current host-based matrix data
*/
void sync_device()
{
size_t bytes = _m * _n * sizeof(value_t);
CUDA_PERROR_EXIT(cudaMemcpy(_d_data, &_h_data[0], bytes, cudaMemcpyHostToDevice));
}
/**
* Synchronize the host-based replica with the current GPU-based matrix data
*/
void sync_host()
{
size_t bytes = _m * _n * sizeof(value_t);
CUDA_PERROR_EXIT(cudaMemcpy(&_h_data[0], _d_data, bytes, cudaMemcpyDeviceToHost));
}
//-----------------------------------------------------------------------------
// Inspectors
//-----------------------------------------------------------------------------
/**
* Return the height of the matrix, subject to the optional \p transpose_op
*/
int height(matrix_transform_t transpose_op = matrix_transform_t::NonTranspose) const
{
switch (transpose_op)
{
case matrix_transform_t::NonTranspose : return _m;
case matrix_transform_t::Transpose : return _n;
default: return -1;
}
}
/**
* Return the width of the matrix, subject to the optional \p transpose_op
*/
int width(matrix_transform_t transpose_op = matrix_transform_t::NonTranspose) const
{
switch (transpose_op)
{
case matrix_transform_t::NonTranspose : return _n;
case matrix_transform_t::Transpose : return _m;
default: return -1;
}
}
/**
* Return item at (x, y) coordinate of matrix, subject to the optional \p transform op
*/
host_value_t get(
int x,
int y,
matrix_transform_t transpose_op = matrix_transform_t::NonTranspose) const
{
switch (transpose_op)
{
case matrix_transform_t::NonTranspose : return _h_data[y + (x * _m)];
case matrix_transform_t::Transpose : return _h_data[x + (y * _m)];
default: return 0;
}
}
/**
* Return the distance (in items) within memory between elements of two
* consecutive columns which have the same row index, subject to the optional \p transform op
*/
int leading_dim(matrix_transform_t transpose_op = matrix_transform_t::NonTranspose) const
{
switch (transpose_op)
{
case matrix_transform_t::NonTranspose : return _m;
case matrix_transform_t::Transpose : return _n;
default: return 0;
}
}
/**
* Get host data pointer
*/
value_t* h_data()
{
return _h_data.data();
}
/**
* Get host data pointer
*/
value_t const* h_data() const
{
return _h_data.data();
}
/**
* Get device data pointer
*/
value_t const* d_data() const
{
return _d_data;
}
/**
* Get device data pointer
*/
value_t * d_data()
{
return _d_data;
}
//-----------------------------------------------------------------------------
// Initialization
//-----------------------------------------------------------------------------
/**
* Initialize matrix values with a 2D "ramp" defined as
* <tt>values(x, y) = (y * rs) + (x * cs)</tt>
*/
void fill_ramp(
host_value_t rs,
host_value_t cs)
{
for (int x = 0; x < _n; x++)
{
for (int y = 0; y < _m; y++)
{
_h_data[y + (x * _m)] = host_value_t((y * rs) + (x * cs));
}
}
}
/**
* Initialize matrix values such that all the elements of the principal diagonal
* are ones and all other elements are zeros
*/
void fill_identity()
{
for (int j = 0; j < _n; j++)
{
for (int i = 0; i < _m; i++)
{
_h_data[i + j * _m] = host_value_t(i == j ? 1 : 0);
}
}
}
/**
* Initialize matrix values using the random number \p generator. The
* \p generator reference is assumed to be a nullary functor that returns
* values convertible to the matrix \p value_t.
*/
template <typename T>
void fill_random(T & generator)
{
for (int j = 0; j < _n; j++)
{
for (int i = 0; i < _m; i++)
{
_h_data[i + j * _m] = (value_t) generator();
}
}
}
/**
* Element-wise matrix addition
*/
matrix & operator+=(matrix const &mat)
{
for (int j = 0; j < _n; j++)
{
for (int i = 0; i < _m; i++)
{
_h_data[i + j * _m] += mat._h_data[i + j * _m];
}
}
return *this;
}
/**
* Element-wise matrix subtraction
*/
matrix & operator-=(matrix const &mat)
{
for (int j = 0; j < _n; j++)
{
for (int i = 0; i < _m; i++)
{
_h_data[i + j * _m] -= mat._h_data[i + j * _m];
}
}
return *this;
}
//-----------------------------------------------------------------------------
// Output
//-----------------------------------------------------------------------------
/**
* Prints matrix in CSV to output stream
*/
template <typename _hv_t>
std::ostream & write_matrix(std::ostream &out, _hv_t)
{
for (int i = 0; i < _m; i++)
{
for (int j = 0; j < _n; j++)
{
out << (j ? "," : "") << _h_data[i + j * _m];
}
out << "\n";
}
return out;
}
/**
* Prints matrix in CSV to output stream
*/
std::ostream & write_matrix(std::ostream &out, int8_t)
{
for (int i = 0; i < _m; i++)
{
for (int j = 0; j < _n; j++)
{
out << (j ? "," : "") << int32_t(_h_data[i + j * _m]);
}
out << "\n";
}
return out;
}
/**
* Prints matrix in CSV to output stream
*/
std::ostream & write_matrix(std::ostream &out)
{
return write_matrix(out, _h_data[0]);
}
//-----------------------------------------------------------------------------
// Floating point "almost-equal" utilities
//-----------------------------------------------------------------------------
static bool almost_equal_ulps(half_t a, half_t b, int max_ulps)
{
if (a == b)
return true;
int32_t int_diff = abs(a.raw() - b.raw());
if (int_diff <= max_ulps)
return true;
return false;
}
static bool almost_equal_ulps(float a, float b, int max_ulps)
{
if (a == b)
return true;
int32_t int_diff = abs(*(int32_t*)&a - *(int32_t*)&b);
if (int_diff <= max_ulps)
return true;
return false;
}
static bool almost_equal_ulps(double a, double b, int max_ulps)
{
if (a == b)
return true;
int64_t int_diff = abs(*(int64_t*)&a - *(int64_t*)&b);
if (int_diff <= max_ulps)
return true;
return false;
}
static bool almost_equal_ulps(int32_t a, int32_t b, int max_ulps)
{
return (a == b);
}
//-----------------------------------------------------------------------------
// matrix operations
//-----------------------------------------------------------------------------
/**
* Returns matrix equality
*/
bool operator==(const matrix<value_t> &mat) const
{
int max_ulps = 30;
if (_m != mat._m || _n != mat._n)
{
fprintf(stderr, "Error: dimension mismatch during matrix comparison.\n"); exit(1);
}
for (int j = 0; j < _n; j++)
{
for (int i = 0; i < _m; i++)
{
if (!almost_equal_ulps(_h_data[i + j * _m], mat._h_data[i + j * _m], max_ulps))
{
return false;
}
}
}
return true;
}
/**
* Returns matrix inequality
*/
bool operator!=(const matrix<value_t> &mat) const
{
return !(*this == mat);
}
/**
* Computes this = (alpha * op(A) * op(B)) + (beta * this), specialized for gemm_nn
*/
template <typename multiplicand_t>
void gemm(
matrix_transform_t transform_a,
matrix_transform_t transform_b,
host_value_t alpha,
const matrix<multiplicand_t> &A,
const matrix<multiplicand_t> &B,
host_value_t beta)
{
// Sanity check dimensions
if ((_m != A.height(transform_a)) ||
(_n != B.width(transform_b)) ||
(A.width(transform_a) != B.height(transform_b)))
{
fprintf(stderr, "Error: dimension mismatch during gemm.\n");
exit(1);
}
int M = A.height(transform_a);
int K = A.width(transform_a);
int N = B.width(transform_b);
// Even the host-side implementation utilizes a blocking structure to improve
// verification performance
int DimBlockM = (M % 16 == 0) ? 16 : 1;
int DimBlockN = (N % 16 == 0) ? 16 : 1;
for (int i = 0; i < M; i += DimBlockM)
{
for (int j = 0; j < N; j += DimBlockN)
{
for (int block_y = 0; block_y < DimBlockM; block_y++)
{
for (int block_x = 0; block_x < DimBlockN; block_x++)
{
int y = i + block_y;
int x = j + block_x;
host_value_t accum(0);
for (int k = 0; k < K; k++)
{
accum += host_value_t(A.get(k, y, transform_a)) * host_value_t(B.get(x, k, transform_b));
}
_h_data[y + x * M] = (alpha * accum) + (beta * _h_data[y + x * M]);
}
}
}
}
}
};
} // namespace cutlass
+99
View File
@@ -0,0 +1,99 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* GPU kernel timer
*/
#include <cuda_runtime.h>
#include <cutlass/util/debug.h>
namespace cutlass {
/******************************************************************************
* gpu_timer
******************************************************************************/
/**
* GPU event-based timer
*/
struct gpu_timer
{
cudaEvent_t _start;
cudaEvent_t _stop;
gpu_timer()
{
CUDA_PERROR_EXIT(cudaEventCreate(&_start));
CUDA_PERROR_EXIT(cudaEventCreate(&_stop));
}
~gpu_timer()
{
CUDA_PERROR_EXIT(cudaEventDestroy(_start));
CUDA_PERROR_EXIT(cudaEventDestroy(_stop));
}
void start()
{
CUDA_PERROR_EXIT(cudaEventRecord(_start, 0));
}
void stop()
{
CUDA_PERROR_EXIT(cudaEventRecord(_stop, 0));
}
float elapsed_millis()
{
float elapsed = 0.0;
CUDA_PERROR_EXIT(cudaEventSynchronize(_stop));
CUDA_PERROR_EXIT(cudaEventElapsedTime(&elapsed, _start, _stop));
return elapsed;
}
};
/******************************************************************************
* sleep_millis
******************************************************************************/
#ifdef _WIN32
#include <windows.h>
void sleep_millis(unsigned milliseconds)
{
Sleep(milliseconds);
}
#else
#include <unistd.h>
void sleep_millis(unsigned milliseconds)
{
usleep(milliseconds * 1000); // takes microseconds
}
#endif
} // namespace cutlass
+155
View File
@@ -0,0 +1,155 @@
/******************************************************************************
* Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are not permitted.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
/**
* \file
* \brief Utilities for converting between types and assessing traits
*/
#include "half.h"
namespace cutlass {
/******************************************************************************
* Float conversion utilities
******************************************************************************/
/// Convert float to value type
template <typename value_t>
value_t from_float(float val)
{
return value_t(val);
}
/// Convert float to value type (__half specialization)
template <>
__half from_float<__half>(float val)
{
return half_t(val);
}
/******************************************************************************
* Type conversion utilities
******************************************************************************/
/// Member \p type is defined as the signed integer type having the same size as \p T
template <typename T>
struct integer_alias;
template <>
struct integer_alias<int8_t> {
using type = int8_t;
};
template <>
struct integer_alias<half_t> {
using type = int16_t;
};
template <>
struct integer_alias<__half> {
using type = int16_t;
};
template <>
struct integer_alias<float> {
using type = int32_t;
};
template <>
struct integer_alias<int> {
using type = int32_t;
};
template <>
struct integer_alias<double> {
using type = int64_t;
};
/******************************************************************************
* Type-info utilities
******************************************************************************/
/// Returns a string to prefix 'gemm' to construct CUBLAS-like kernel names
template <math_operation_class_t math_op, typename value_t, typename accum_t> char const *to_prefix_string();
template <> char const *to_prefix_string<math_operation_class_t::scalar, half_t, half_t>() {
return "H";
}
template <> char const *to_prefix_string<math_operation_class_t::scalar, __half, __half>() {
return "H";
}
template <> char const *to_prefix_string<math_operation_class_t::scalar, float, float>() {
return "S";
}
template <> char const *to_prefix_string<math_operation_class_t::matrix, __half, __half>() {
return "WmmaH";
}
template <> char const *to_prefix_string<math_operation_class_t::matrix, __half, float>() {
return "WmmaS";
}
template <> char const *to_prefix_string<math_operation_class_t::scalar, double, double>() {
return "D";
}
template <> char const *to_prefix_string<math_operation_class_t::scalar, int8_t, int32_t>() {
return "I";
}
/******************************************************************************
* Maps value_t to the minimum vector size used to load operand
******************************************************************************/
template <typename T>
struct operand_load_type;
template <>
struct operand_load_type<int8_t> { using type = int32_t; };
template <typename T>
struct operand_load_type { using type = T; };
/******************************************************************************
* Minimum alignment requirement, if any, determined from value_t.
******************************************************************************/
template <typename value_t>
struct gemm_alignment_requirement;
template <>
struct gemm_alignment_requirement<uint8_t> { static const int value = 4; };
template <typename value_t>
struct gemm_alignment_requirement { static const int value = 0; };
} // namespace cutlass