CUTLASS 2.7 (#318)

CUTLASS 2.7

Mainloop fusion for GEMM: summation over A or B
Strided DGRAD (optimized iterators)
Half-precision GELU_taylor activation functions
Use these when accumulation and epilogue compute types are all cutlass::half_t
Tuning and bug fixes to fused GEMM + GEMM example
Support for smaller than 128b aligned Convolutions: see examples
Caching of results to accelerate Convolution unit tests
Can be enabled or disabled by running cmake .. -DCUTLASS_TEST_ENABLE_CACHED_RESULTS=OFF
Corrections and bug fixes reported by the CUTLASS community
Thank you for filing these issues!

authored-by: Haicheng Wu haichengw@nvidia.com, Manish Gupta manigupta@nvidia.com, Dustyn Blasig dblasig@nvidia.com, Andrew Kerr akerr@nvidia.com
This commit is contained in:
Manish Gupta
2021-09-20 11:02:22 -07:00
committed by GitHub
parent 9ac255863f
commit 2e07c4cc2f
62 changed files with 5611 additions and 186 deletions

View File

@@ -134,7 +134,7 @@ cutlass_test_unit_add_executable(
conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm70.cu
)
# Conv2d - F16 input, F32 output, F32 accumulation - SM75
# Conv - F16 input, F32 output, F32 accumulation - SM75
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_f32_sm75
@@ -144,11 +144,13 @@ cutlass_test_unit_add_executable(
conv2d_fprop_with_broadcast_sm75.cu
conv2d_fprop_with_reduction_sm75.cu
conv3d_wgrad_implicit_gemm_f16ndhwc_f16ndhwc_f32ndhwc_tensor_op_f32_sm75.cu
)
if (CUTLASS_NVCC_MAX_ARCH GREATER_EQUAL 80)
# Conv2d - F16 input, F16 output, F16 accumulation
# Conv - F16 input, F16 output, F16 accumulation
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_f16_sm80
@@ -157,24 +159,23 @@ if (CUTLASS_NVCC_MAX_ARCH GREATER_EQUAL 80)
conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.cu
)
# Conv2d - F16 input, F32 output, F32 accumulation
# Conv - F16 input, F32 output, F32 accumulation
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_f32_sm80
# Conv2d
conv2d_fprop_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.cu
conv2d_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.cu
conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.cu
conv3d_wgrad_implicit_gemm_f16ndhwc_f16ndhwc_f32ndhwc_tensor_op_f32_sm75.cu
conv3d_wgrad_implicit_gemm_f16ndhwc_f16ndhwc_f32ndhwc_tensor_op_f32_sm80.cu
# Strided Dgrad
# Conv2d (Strided Dgrad)
conv2d_strided_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.cu
# Conv3d
conv3d_wgrad_implicit_gemm_f16ndhwc_f16ndhwc_f32ndhwc_tensor_op_f32_sm80.cu
)
# Conv2d - TF32 input, F32 output, F32 accumulation
# Conv - TF32 input, F32 output, F32 accumulation
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_f32_tf32_sm80
@@ -192,7 +193,6 @@ endif()
if (CUTLASS_NVCC_MAX_ARCH GREATER_EQUAL 75)
# Conv2d - S8 input, S32 output, S32 accumulation
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_s32
conv2d_fprop_implicit_gemm_s8nhwc_s8nhwc_s32nhwc_tensor_op_s32_sm75.cu
@@ -200,7 +200,6 @@ if (CUTLASS_NVCC_MAX_ARCH GREATER_EQUAL 75)
)
# Conv2d - S8 interleaved input, S8 interleaved output, S32 accumulation
cutlass_test_unit_add_executable(
cutlass_test_unit_conv_device_tensorop_s32_interleaved
conv2d_fprop_implicit_gemm_s8ncxhwx_s8cxrskx_s8ncxhwx_tensor_op_s32_sm75.cu

View File

@@ -0,0 +1,791 @@
/***************************************************************************************************
* Copyright (c) 2017-2021, 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 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.
*
**************************************************************************************************/
/*! \file
\brief Helper to construct cached name for
*/
#pragma once
#include <typeinfo>
#include <fstream>
#include <list>
#include <utility>
#include <sstream>
#include "cutlass/cutlass.h"
#include "cutlass/layout/matrix.h"
#include "cutlass/conv/convolution.h"
#include "cutlass/conv/conv2d_problem_size.h"
#include "cutlass/conv/conv3d_problem_size.h"
#include "cutlass/core_io.h"
#include "cutlass/util/tensor_view_io.h"
#ifndef CUTLASS_TEST_ENABLE_CACHED_RESULTS
#define CUTLASS_TEST_ENABLE_CACHED_RESULTS false
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////
namespace test {
namespace conv {
namespace device {
/////////////////////////////////////////////////////////////////////////////////////////////////
/// Result of a test
struct CachedTestKey {
std::string op; ///< Concatenated string representation of operation performed
std::string problem; ///< Concatenated string representation of problem description
std::string types; ///< Concatenated string representation of operand types
uint32_t A; ///< Hashed result of tensor A
uint32_t B; ///< Hashed result of tensor B
uint32_t C; ///< Hashed result of tensor C
//
// Methods
//
inline CachedTestKey(): A(), B(), C() { }
inline CachedTestKey(
std::string op, ///< Concatenated string representation of operation performed
std::string problem, ///< Concatenated string representation of problem description
std::string types, ///< Concatenated string representation of operand types
uint32_t A, ///< Hashed result of tensor A
uint32_t B, ///< Hashed result of tensor B
uint32_t C ///< Hashed result of tensor C
):
op(op), problem(problem), types(types), A(A), B(B), C(C)
{ }
/// Checks for equality of the problem
bool operator==(CachedTestKey const &rhs) const {
return op == rhs.op && problem == rhs.problem && types == rhs.types && A == rhs.A && B == rhs.B && C == rhs.C;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
inline std::istream &operator>>(std::istream &in, CachedTestKey &result) {
in >> result.op;
in >> result.problem;
in >> result.types;
in >> result.A;
in >> result.B;
in >> result.C;
return in;
}
inline std::ostream &operator<<(std::ostream &out, CachedTestKey const &result) {
out << result.op << " ";
out << result.problem << " ";
out << result.types << " ";
out << result.A << " ";
out << result.B << " ";
out << result.C << " ";
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
struct CachedTestResult {
uint32_t D;
//
// Methods
//
CachedTestResult(): D() { }
CachedTestResult(uint32_t D): D(D) { }
operator bool() const {
return bool(D);
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
inline std::istream &operator>>(std::istream &in, CachedTestResult &result) {
in >> result.D;
return in;
}
inline std::ostream &operator<<(std::ostream &out, CachedTestResult const &result) {
out << result.D;
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
struct CachedTestResultListing {
std::list<std::pair<CachedTestKey, CachedTestResult>> results;
//
// Methods
//
inline CachedTestResultListing(std::string const &path) {
std::ifstream file(path);
while (file.good()) {
CachedTestKey key;
file >> key;
CachedTestResult result;
file >> result;
if (result) {
results.push_back(std::make_pair(key, result));
}
}
}
/// Returns the cached result
std::pair<bool, CachedTestResult> find(CachedTestKey const &rhs) const {
for (auto const & result : results) {
if (result.first == rhs) {
return std::make_pair(true, result.second);
}
}
return std::make_pair(false, CachedTestResult());
}
/// Appends an entry
void append(CachedTestKey const &key, CachedTestResult const &result) {
if (result) {
results.push_back(std::make_pair(key, result));
}
}
/// Writes the entire listing to a file
bool write(std::string const &path) {
std::ofstream file(path);
if (!file.good()) {
return false;
}
for (auto const &result : results) {
file << result.first << result.second << std::endl;
}
return true;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Element>
struct ScalarEncoder {
Element scalar;
ScalarEncoder(Element s): scalar(s) { }
std::string str() const {
std::stringstream ss;
Element s = scalar;
if (s < Element()) {
s = -s;
ss << "n";
}
ss << s;
return ss.str();
}
};
template <typename Element>
ScalarEncoder<Element> EncodeScalar(Element a) {
return ScalarEncoder<Element>(a);
}
template <typename Element>
struct ScalarEncoder<cutlass::complex<Element>> {
cutlass::complex<Element> scalar;
ScalarEncoder(cutlass::complex<Element> s): scalar(s) { }
std::string str() const {
std::stringstream ss;
ss << EncodeScalar<Element>(scalar.real()) << "_" << EncodeScalar<Element>(scalar.imag()) << "i";
return ss.str();
}
};
template <typename Element>
std::ostream &operator<<(std::ostream &out, ScalarEncoder<Element> const &scalar) {
out << scalar.str();
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
inline char const *EncodeOperator(cutlass::conv::Operator conv_op) {
switch (conv_op) {
case cutlass::conv::Operator::kFprop: return "fprop";
case cutlass::conv::Operator::kDgrad: return "dgrad";
case cutlass::conv::Operator::kWgrad: return "wgrad";
}
return "conv_unknown";
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Encode GemmCoord (Gemm problem size)
inline std::ostream &EncodeProblemSize(
std::ostream &out,
cutlass::gemm::GemmCoord const &problem) {
out << problem.m() << "x" << problem.n() << "x" << problem.k() << "_";
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Encode Conv2dProblemSize
inline std::ostream &EncodeProblemSize(
std::ostream &out,
cutlass::conv::Conv2dProblemSize const &problem) {
out << problem.N << "x" << problem.H << "x" << problem.W << "x" << problem.C << "_"
<< problem.P << "x" << problem.Q << "_" << problem.K << "x" << problem.R << "x" << problem.S << "_";
out << "pad_h" << problem.pad_h << "w" << problem.pad_w << "_";
out << "stride_h" << problem.stride_h << "w" << problem.stride_w << "_";
out << "dil_h" << problem.dilation_h << "w" << problem.dilation_w << "_";
switch (problem.mode) {
case cutlass::conv::Mode::kCrossCorrelation:
out << "corr";
break;
case cutlass::conv::Mode::kConvolution:
out << "conv";
break;
}
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Encode Conv3dProblemSize
inline std::ostream &EncodeProblemSize(
std::ostream &out,
cutlass::conv::Conv3dProblemSize const &problem) {
out << problem.N << "x" << problem.D << "x" << problem.H << "x" << problem.W << "x" << problem.C << "_"
<< problem.Z << problem.P << "x" << problem.Q << "_" << problem.K << "x" << problem.R << "x" << problem.S << "_";
out << "pad_d" << problem.pad_h << "h" << problem.pad_h << "w" << problem.pad_w << "_";
out << "stride_d" << problem.stride_d << "h" << problem.stride_h << "w" << problem.stride_w << "_";
out << "dil_d" << problem.dilation_d << "h" << problem.dilation_h << "w" << problem.dilation_w << "_";
switch (problem.mode) {
case cutlass::conv::Mode::kCrossCorrelation:
out << "corr";
break;
case cutlass::conv::Mode::kConvolution:
out << "conv";
break;
}
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Element>
inline std::string ElementTypeName() {
return std::string(typeid(Element).name());
}
template <>
inline std::string ElementTypeName<cutlass::half_t>() {
return "h";
}
template <>
inline std::string ElementTypeName<cutlass::complex<cutlass::half_t>>() {
return "ch";
}
template <>
inline std::string ElementTypeName<cutlass::bfloat16_t>() {
return "bf16";
}
template <>
inline std::string ElementTypeName<cutlass::complex<cutlass::bfloat16_t>>() {
return "cbf16";
}
template <>
inline std::string ElementTypeName<cutlass::tfloat32_t>() {
return "tf32";
}
template <>
inline std::string ElementTypeName<cutlass::complex<cutlass::tfloat32_t>>() {
return "ctf32";
}
template <>
inline std::string ElementTypeName<cutlass::complex<float>>() {
return "c";
}
template <>
inline std::string ElementTypeName<cutlass::complex<double>>() {
return "z";
}
template <>
inline std::string ElementTypeName<cutlass::Quaternion<float>>() {
return "q";
}
template <>
inline std::string ElementTypeName<int8_t>() {
return "s8";
}
template <>
inline std::string ElementTypeName<uint8_t>() {
return "u8";
}
template <>
inline std::string ElementTypeName<cutlass::int4b_t>() {
return "s4";
}
template <>
inline std::string ElementTypeName<cutlass::uint4b_t>() {
return "u4";
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Layout>
inline std::string LayoutTypeName() {
return std::string(typeid(Layout).name());
}
template <>
inline std::string LayoutTypeName<cutlass::layout::ColumnMajor>() {
return "n";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::RowMajor>() {
return "t";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorNHWC>() {
return "nhwc";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorNCxHWx<32>>() {
return "nc32hw32";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorNCxHWx<64>>() {
return "nc64hw64";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorCxRSKx<32>>() {
return "c32rsk32";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorCxRSKx<64>>() {
return "c64rsk64";
}
template <>
inline std::string LayoutTypeName<cutlass::layout::TensorNDHWC>() {
return "ndhwc";
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Element, typename Layout>
inline std::string TensorTypeName() {
std::stringstream ss;
ss << ElementTypeName<Element>() << LayoutTypeName<Layout>();
return ss.str();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/// Hash function on a byte array
struct CRC32 {
uint32_t table[256];
//
// Methods
//
CRC32() {
uint32_t rem;
int i, j;
for (i = 0; i < 256; i++) {
rem = i;
for (j = 0; j < 8; j++) {
if (rem & 1) {
rem >>= 1;
rem ^= 0xedb88320;
} else
rem >>= 1;
}
table[i] = rem;
}
}
/// Computes the CRC of an array of bytes
uint32_t operator()(void const *start, size_t length, uint32_t crc = uint32_t()) const {
uint8_t const *p = static_cast<uint8_t const *>(start);
uint8_t const *q = static_cast<uint8_t const *>(start) + length;
crc = ~crc;
for (; p != q; ++p) {
uint8_t octet = *p;
crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet];
}
return ~crc;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename Element, typename Layout
>
uint32_t TensorHash(
cutlass::TensorView<Element, Layout> view,
CRC32 const &hash = CRC32(),
uint32_t crc = uint32_t()
) {
return hash(view.data(), view.capacity() * cutlass::sizeof_bits<Element>::value / 8, crc);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline std::ostream &EncodeTypes(
std::ostream &out
) {
out << TensorTypeName<ElementA, LayoutA>() << "_"
<< TensorTypeName<ElementB, LayoutB>() << "_"
<< TensorTypeName<ElementC, LayoutC>() << "_"
<< ElementTypeName<ElementAccumulator>() << "_"
<< ElementTypeName<ElementCompute>();
return out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline CachedTestKey CreateCachedGemmTestKey(
cutlass::gemm::GemmCoord const &problem,
ElementCompute alpha,
ElementCompute beta,
cutlass::TensorView<ElementA, LayoutA> A,
cutlass::TensorView<ElementA, LayoutB> B,
cutlass::TensorView<ElementC, LayoutC> C
) {
CachedTestKey key;
// Encode gemm operator and problem sizes
key.op = "gemm";
std::stringstream ss_problem;
EncodeProblemSize(ss_problem, problem);
ss_problem << "_alpha" << EncodeScalar(alpha) << "_beta" << EncodeScalar(beta);
key.problem = ss_problem.str();
// Encode problem data types
std::stringstream ss_types;
EncodeTypes<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute>(ss_types);
key.types = ss_types.str();
// Encode hash for problem data
CRC32 crc_hash;
key.A = TensorHash(A, crc_hash);
key.B = TensorHash(B, crc_hash);
key.C = TensorHash(C, crc_hash);
return key;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline CachedTestKey CreateCachedConv2dTestKey(
cutlass::conv::Operator conv_operator,
cutlass::conv::Conv2dProblemSize const &problem,
ElementCompute alpha,
ElementCompute beta,
cutlass::TensorView<ElementA, LayoutA> A,
cutlass::TensorView<ElementA, LayoutB> B,
cutlass::TensorView<ElementC, LayoutC> C
) {
CachedTestKey key;
// Encode conv2d operator and problem sizes
key.op = "conv2d";
std::stringstream ss_problem;
ss_problem << EncodeOperator(conv_operator) << "_";
EncodeProblemSize(ss_problem, problem);
ss_problem << "_alpha" << EncodeScalar(alpha) << "_beta" << EncodeScalar(beta);
key.problem = ss_problem.str();
// Encode problem data types
std::stringstream ss_types;
EncodeTypes<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute>(ss_types);
key.types = ss_types.str();
// Encode hash for problem data
CRC32 crc_hash;
key.A = TensorHash(A, crc_hash);
key.B = TensorHash(B, crc_hash);
key.C = TensorHash(C, crc_hash);
return key;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline CachedTestKey CreateCachedConv2dWithBroadcastTestKey(
cutlass::conv::Operator conv_operator,
cutlass::conv::Conv2dProblemSize const &problem,
ElementCompute alpha,
ElementCompute beta,
cutlass::TensorView<ElementA, LayoutA> A,
cutlass::TensorView<ElementA, LayoutB> B,
cutlass::TensorView<ElementC, LayoutC> C
) {
CachedTestKey key;
// Encode conv2d operator and problem sizes
key.op = "conv2d_with_broadcast";
std::stringstream ss_problem;
ss_problem << EncodeOperator(conv_operator) << "_";
EncodeProblemSize(ss_problem, problem);
ss_problem << "_alpha" << EncodeScalar(alpha) << "_beta" << EncodeScalar(beta);
key.problem = ss_problem.str();
// Encode problem data types
std::stringstream ss_types;
EncodeTypes<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute>(ss_types);
key.types = ss_types.str();
// Encode hash for problem data
CRC32 crc_hash;
key.A = TensorHash(A, crc_hash);
key.B = TensorHash(B, crc_hash);
key.C = TensorHash(C, crc_hash);
return key;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline CachedTestKey CreateCachedConv2dWithReductionTestKey(
cutlass::conv::Operator conv_operator,
cutlass::conv::Conv2dProblemSize const &problem,
ElementCompute alpha,
ElementCompute beta,
cutlass::TensorView<ElementA, LayoutA> A,
cutlass::TensorView<ElementA, LayoutB> B,
cutlass::TensorView<ElementC, LayoutC> C
) {
CachedTestKey key;
// Encode conv2d operator and problem sizes
key.op = "conv2d_with_reduction";
std::stringstream ss_problem;
ss_problem << EncodeOperator(conv_operator) << "_";
EncodeProblemSize(ss_problem, problem);
ss_problem << "_alpha" << EncodeScalar(alpha) << "_beta" << EncodeScalar(beta);
key.problem = ss_problem.str();
// Encode problem data types
std::stringstream ss_types;
EncodeTypes<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute>(ss_types);
key.types = ss_types.str();
// Encode hash for problem data
CRC32 crc_hash;
key.A = TensorHash(A, crc_hash);
key.B = TensorHash(B, crc_hash);
key.C = TensorHash(C, crc_hash);
return key;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
template <
typename ElementA, typename LayoutA,
typename ElementB, typename LayoutB,
typename ElementC, typename LayoutC,
typename ElementAccumulator,
typename ElementCompute
>
inline CachedTestKey CreateCachedConv3dTestKey(
cutlass::conv::Operator conv_operator,
cutlass::conv::Conv3dProblemSize const &problem,
ElementCompute alpha,
ElementCompute beta,
cutlass::TensorView<ElementA, LayoutA> A,
cutlass::TensorView<ElementA, LayoutB> B,
cutlass::TensorView<ElementC, LayoutC> C
) {
CachedTestKey key;
// Encode conv3d operator and problem sizes
key.op = "conv3d";
std::stringstream ss_problem;
ss_problem << EncodeOperator(conv_operator) << "_";
EncodeProblemSize(ss_problem, problem);
ss_problem << "_alpha" << EncodeScalar(alpha) << "_beta" << EncodeScalar(beta);
key.problem = ss_problem.str();
// Encode problem data types
std::stringstream ss_types;
EncodeTypes<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute>(ss_types);
key.types = ss_types.str();
// Encode problem data
CRC32 crc_hash;
key.A = TensorHash(A, crc_hash);
key.B = TensorHash(B, crc_hash);
key.C = TensorHash(C, crc_hash);
return key;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace device
} // nammespace conv
} // namespace test
/////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -170,6 +170,25 @@ TEST(SM80_Device_Conv2d_Fprop_Analytic_ImplicitGemm_f16nhwc_f16nhwc_f16nhwc_tens
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 4, 4, 14}, // input size (NHWC)
{8, 3, 3, 14}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 23, 56, 98}, // input size (NHWC)
{128, 3, 3, 98}, // filter size (KRSC)
{4, 0, 5, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
/// Run all unit test sizes with device-level Conv2d instance
EXPECT_TRUE(test::conv::device::TestAllConv2d<Conv2dFprop>(problem_size_list));
}
@@ -225,6 +244,25 @@ TEST(SM80_Device_Conv2d_Fprop_Optimized_ImplicitGemm_f16nhwc_f16nhwc_f16nhwc_ten
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 4, 4, 14}, // input size (NHWC)
{8, 3, 3, 14}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 23, 56, 98}, // input size (NHWC)
{128, 3, 3, 98}, // filter size (KRSC)
{4, 0, 5, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
/// Run all unit test sizes with device-level Conv2d instance
EXPECT_TRUE(test::conv::device::TestAllConv2d<Conv2dFprop>(problem_size_list));
}
@@ -280,6 +318,24 @@ TEST(SM80_Device_Conv2d_Fprop_Optimized_ImplicitGemm_f16nhwc_f16nhwc_f16nhwc_ten
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 4, 4, 28}, // input size (NHWC)
{8, 3, 3, 28}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 23, 56, 100}, // input size (NHWC)
{128, 3, 3, 100}, // filter size (KRSC)
{4, 0, 5, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
/// Run all unit test sizes with device-level Conv2d instance
EXPECT_TRUE(test::conv::device::TestAllConv2d<Conv2dFprop>(problem_size_list));
}

View File

@@ -182,7 +182,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{1, 7, 8, minimum_channel_size}, // input size (NHWC)
{8, 3, 3, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -190,7 +190,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{1, 7, 9, minimum_channel_size}, // input size (NHWC)
{8, 4, 4, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -198,7 +198,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{2, 7, 9, minimum_channel_size}, // input size (NHWC)
{8, 5, 5, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -206,7 +206,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{3, 7, 9, minimum_channel_size}, // input size (NHWC)
{8, 6, 5, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -214,7 +214,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{3, 7, 9, minimum_channel_size}, // input size (NHWC)
{8, 6, 6, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -222,7 +222,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{3, 7, 9, minimum_channel_size}, // input size (NHWC)
{8, 7, 7, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -234,7 +234,7 @@ struct TestbedConv2dProblemSizes {
// C < CTA::K and non-multiples of CTA::K. Typical CTA::K = {32, 64}
////////////////////////////////////////////////////////////////////////////////////////////
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 11, 11, minimum_channel_size}, // input size (NHWC)
{1, 11, 7, minimum_channel_size}, // input size (NHWC)
{8, 1, 1, minimum_channel_size}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -242,7 +242,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 11, 11, minimum_channel_size}, // input size (NHWC)
{1, 11, 7, minimum_channel_size}, // input size (NHWC)
{8, 3, 3, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -250,7 +250,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 13, 13, minimum_channel_size}, // input size (NHWC)
{1, 13, 11, minimum_channel_size}, // input size (NHWC)
{8, 1, 1, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -258,40 +258,40 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, minimum_channel_size}, // input size (NHWC)
{8, 2, 2, minimum_channel_size}, // filter size (KRSC)
{1, 17, 19, minimum_channel_size}, // input size (NHWC)
{16, 2, 2, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 5, 5, minimum_channel_size}, // input size (NHWC)
{8, 3, 3, minimum_channel_size}, // filter size (KRSC)
{1, 23, 5, minimum_channel_size}, // input size (NHWC)
{16, 3, 3, minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, 8}, // input size (NHWC)
{8, 3, 3, 8}, // filter size (KRSC)
{1, 13, 17, 8}, // input size (NHWC)
{24, 3, 3, 8}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 8, 8, 8}, // input size (NHWC)
{8, 3, 3, 8}, // filter size (KRSC)
{1, 23, 21, 8}, // input size (NHWC)
{24, 3, 3, 8}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 16, 16, 8}, // input size (NHWC)
{8, 3, 3, 8}, // filter size (KRSC)
{1, 20, 24, 8}, // input size (NHWC)
{40, 3, 3, 8}, // filter size (KRSC)
{3, 3, 3, 3}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
@@ -352,7 +352,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 16, 16, 64 + minimum_channel_size}, // input size (NHWC)
{1, 16, 24, 64 + minimum_channel_size}, // input size (NHWC)
{96, 3, 3, 64 + minimum_channel_size}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
@@ -363,7 +363,7 @@ struct TestbedConv2dProblemSizes {
// Medium input size, filter size (1x1, 3,x3, 5x5, 7x7), stride (2, 2)
////////////////////////////////////////////////////////////////////////////////////
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 16, 16, 288}, // input size (NHWC)
{1, 13, 16, 288}, // input size (NHWC)
{160, 5, 5, 288}, // filter size (KRSC)
{2, 2, 2, 2}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -371,7 +371,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 55, 55, 256}, // input size (NHWC)
{1, 55, 51, 256}, // input size (NHWC)
{512, 1, 1, 256}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -379,7 +379,7 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 80, 80, 32}, // input size (NHWC)
{1, 71, 80, 32}, // input size (NHWC)
{64, 5, 5, 32}, // filter size (KRSC)
{2, 2, 2, 2}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
@@ -398,7 +398,7 @@ struct TestbedConv2dProblemSizes {
// Medium input size stride (3, 3), filter (3, 3), non-default padding
////////////////////////////////////////////////////////////////////////////////////
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{1, 27, 27, 256}, // input size (NHWC)
{1, 27, 23, 256}, // input size (NHWC)
{512, 3, 3, 256}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{3, 3}, // stride (stride_h, stride_w)
@@ -464,16 +464,16 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{32, 32, 32, 32}, // input size (NHWC)
{32, 1, 1, 32}, // filter size (KRSC)
{32, 24, 32, 32}, // input size (NHWC)
{32, 1, 2, 32}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{4, 3, 3, 128}, // input size (NHWC)
{256, 3, 3, 128}, // filter size (KRSC)
{4, 4, 5, 128}, // input size (NHWC)
{256, 3, 6, 128}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
{1, 1}, // dilation (dilation_h, dilation_w)
@@ -481,8 +481,8 @@ struct TestbedConv2dProblemSizes {
));
conv2d_default_sizes.push_back(cutlass::conv::Conv2dProblemSize(
{4, 1, 1, 256}, // input size (NHWC)
{328, 3, 3, 256}, // filter size (KRSC)
{4, 2, 3, 256}, // input size (NHWC)
{328, 3, 5, 256}, // filter size (KRSC)
{1, 1, 1, 1}, // padding (pad_h, _, pad_w, _)
{1, 1}, // stride (stride_h, stride_w)
{1, 1}, // dilation (dilation_h, dilation_w)

View File

@@ -231,4 +231,137 @@ TEST(SM80_Device_Conv2d_Strided_Dgrad_Analytic_ImplicitGemm_f16nhwc_f16nhwc_f32n
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Strided Dgrad (Optimized)
////////////////////////////////////////////////////////////////////////////////
TEST(SM80_Device_Conv2d_Strided_Dgrad_Optimized_ImplicitGemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32,
128x128_32x3_64x64x32) {
/// Conv operation element types for the Gemm equivalent (ImplicitGemm)
using ElementA = cutlass::half_t;
using ElementB = cutlass::half_t;
using ElementC = float;
using ElementAccumulator = float;
using ElementCompute = float;
/// Device-level Conv2d instance
using Conv2dDgradKernel = typename cutlass::conv::kernel::DefaultConv2dDgrad<
ElementA, cutlass::layout::TensorNHWC,
ElementB, cutlass::layout::TensorNHWC,
ElementC, cutlass::layout::TensorNHWC,
ElementAccumulator,
cutlass::arch::OpClassTensorOp,
cutlass::arch::Sm80,
cutlass::gemm::GemmShape<128, 128, 32>,
cutlass::gemm::GemmShape<64, 64, 32>,
cutlass::gemm::GemmShape<16, 8, 16>,
cutlass::epilogue::thread::LinearCombination<
ElementC,
128 / cutlass::sizeof_bits<ElementC>::value,
ElementAccumulator,
ElementCompute
>,
cutlass::conv::threadblock::StridedDgradIdentityThreadblockSwizzle<>,
3,
cutlass::arch::OpMultiplyAdd,
cutlass::conv::IteratorAlgorithm::kOptimized,
cutlass::conv::StrideSupport::kStrided
>::Kernel;
using Conv2dDgrad = cutlass::conv::device::ImplicitGemmConvolution<Conv2dDgradKernel>;
test::conv::device::Conv2dProblemVector problem_size_list;
#if 0 // run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 56, 56, 8}, // input size (NHWC)
{8, 1, 1, 8}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 55, 55, 8}, // input size (NHWC)
{8, 1, 1, 8}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
#endif
/// Run all unit test sizes with device-level Conv2d instance
EXPECT_TRUE(test::conv::device::TestAllConv2d<Conv2dDgrad>(problem_size_list));
}
////////////////////////////////////////////////////////////////////////////////
TEST(SM80_Device_Conv2d_Strided_Dgrad_Optimized_ImplicitGemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_align4,
128x128_32x3_64x64x32) {
/// Conv operation element types for the Gemm equivalent (ImplicitGemm)
using ElementA = cutlass::half_t;
using ElementB = cutlass::half_t;
using ElementC = float;
using ElementAccumulator = float;
using ElementCompute = float;
/// Device-level Conv2d instance
using Conv2dDgradKernel = typename cutlass::conv::kernel::DefaultConv2dDgrad<
ElementA, cutlass::layout::TensorNHWC,
ElementB, cutlass::layout::TensorNHWC,
ElementC, cutlass::layout::TensorNHWC,
ElementAccumulator,
cutlass::arch::OpClassTensorOp,
cutlass::arch::Sm80,
cutlass::gemm::GemmShape<128, 128, 32>,
cutlass::gemm::GemmShape<64, 64, 32>,
cutlass::gemm::GemmShape<16, 8, 16>,
cutlass::epilogue::thread::LinearCombination<
ElementC,
4,
ElementAccumulator,
ElementCompute
>,
cutlass::conv::threadblock::StridedDgradIdentityThreadblockSwizzle<>,
3,
cutlass::arch::OpMultiplyAdd,
cutlass::conv::IteratorAlgorithm::kOptimized,
cutlass::conv::StrideSupport::kStrided,
4,
4
>::Kernel;
using Conv2dDgrad = cutlass::conv::device::ImplicitGemmConvolution<Conv2dDgradKernel>;
test::conv::device::Conv2dProblemVector problem_size_list;
// run specific problem size in the unit test first
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 56, 56, 12}, // input size (NHWC)
{8, 1, 1, 12}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
problem_size_list.push_back(cutlass::conv::Conv2dProblemSize(
{1, 55, 55, 12}, // input size (NHWC)
{8, 1, 1, 12}, // filter size (KRSC)
{0, 0, 0, 0}, // padding (pad_h, _, pad_w, _)
{2, 2}, // stride (stride_h, stride_w)
{1, 1} // dilation (dilation_h, dilation_w)
));
/// Run all unit test sizes with device-level Conv2d instance
EXPECT_TRUE(test::conv::device::TestAllConv2d<Conv2dDgrad>(problem_size_list));
}
////////////////////////////////////////////////////////////////////////////////
#endif // CUTLASS_ARCH_MMA_SM80_SUPPORTED

View File

@@ -47,6 +47,8 @@
#include "cutlass/core_io.h"
#include "cutlass/util/tensor_view_io.h"
#include "cache_testbed_output.h"
namespace test {
namespace conv {
namespace device {
@@ -116,7 +118,6 @@ public:
cutlass::Distribution::Kind dist_kind,
uint64_t seed) {
//cutlass::reference::host::TensorFill(view, Element(1.0f));
if (dist_kind == cutlass::Distribution::Uniform) {
int scope;
@@ -126,7 +127,12 @@ public:
scope = 2;
}
else if (bits == 16) {
scope = 3;
if (cutlass::sizeof_bits<ElementAccumulator>::value <= 16) {
scope = 3;
}
else {
scope = 5;
}
}
else {
scope = 8;
@@ -137,6 +143,7 @@ public:
else if (dist_kind == cutlass::Distribution::Identity) {
cutlass::reference::host::TensorFillIdentity(view);
}
else if (dist_kind == cutlass::Distribution::Gaussian) {
@@ -321,6 +328,50 @@ public:
tensor_D_computed.sync_host();
//
// Reference check - support caching results
//
CachedTestKey cached_test_key = CreateCachedConv2dTestKey<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute
>(
kConvolutionalOperator,
problem_size,
alpha,
beta,
tensor_A.host_view(),
tensor_B.host_view(),
tensor_C.host_view()
);
//
// Look for the cached key
//
bool cached_result_loaded = false;
CachedTestResult cached_test_result;
std::string conv2d_result_cache_name =
std::string("cached_results_") + CUTLASS_TARGET_NAME + ".txt";
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
CachedTestResultListing cached_results(conv2d_result_cache_name);
auto cached = cached_results.find(cached_test_key);
cached_result_loaded = cached.first;
if (cached_result_loaded) {
cached_test_result = cached.second;
}
}
if (!cached_result_loaded) {
#if CUTLASS_CONV_TEST_UNIT_REFERENCE_DEVICE_ENABLED
cutlass::reference::device::Conv2d<
@@ -367,9 +418,32 @@ public:
beta);
#endif
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
cached_test_result.D = TensorHash(tensor_D_reference.host_view());
CachedTestResultListing cached_results(conv2d_result_cache_name);
cached_results.append(cached_test_key, cached_test_result);
cached_results.write(conv2d_result_cache_name);
}
} // if (!cached_result_loaded)
uint32_t tensor_D_hash = TensorHash(tensor_D_computed.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
passed = (tensor_D_hash == cached_test_result.D);
EXPECT_EQ(tensor_D_hash, cached_test_result.D)
<< "Hash-based comparison failed for key:" << "\n" << cached_test_key << "\n";
}
else {
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
}
EXPECT_TRUE(passed);
@@ -416,9 +490,18 @@ public:
results
<< "\nA:\n" << tensor_A.host_view() << "\n"
<< "\nB:\n" << tensor_B.host_view() << "\n"
<< "\nC:\n" << tensor_C.host_view() << "\n"
<< "\nD reference:\n" << tensor_D_reference.host_view() << "\n"
<< "\nD computed:\n" << tensor_D_computed.host_view() << "\n";
<< "\nC:\n" << tensor_C.host_view() << "\n";
results << "\nD reference (hash: " << cached_test_result.D << ")\n";
if (!cached_result_loaded) {
results
<< tensor_D_reference.host_view() << "\n";
}
results
<< "\nD computed (hash: " << tensor_D_hash << ")\n"
<< tensor_D_computed.host_view() << "\n";
}

View File

@@ -48,6 +48,8 @@
#include "cutlass/core_io.h"
#include "cutlass/util/tensor_view_io.h"
#include "cache_testbed_output.h"
namespace test {
namespace conv {
namespace device {
@@ -280,6 +282,50 @@ public:
tensor_D_computed.sync_host();
//
// Reference check - support caching results
//
CachedTestKey cached_test_key = CreateCachedConv2dTestKey<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute
>(
kConvolutionalOperator,
problem_size,
alpha,
beta,
tensor_A.host_view(),
tensor_B.host_view(),
tensor_C.host_view()
);
//
// Look for the cached key
//
bool cached_result_loaded = false;
CachedTestResult cached_test_result;
std::string conv2d_result_cache_name =
std::string("cached_results_") + CUTLASS_TARGET_NAME + ".txt";
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
CachedTestResultListing cached_results(conv2d_result_cache_name);
auto cached = cached_results.find(cached_test_key);
cached_result_loaded = cached.first;
if (cached_result_loaded) {
cached_test_result = cached.second;
}
}
if (!cached_result_loaded) {
#if CUTLASS_CONV_TEST_UNIT_REFERENCE_DEVICE_ENABLED
cutlass::reference::device::Conv2d<
@@ -332,9 +378,32 @@ public:
beta);
#endif
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
cached_test_result.D = TensorHash(tensor_D_reference.host_view());
CachedTestResultListing cached_results(conv2d_result_cache_name);
cached_results.append(cached_test_key, cached_test_result);
cached_results.write(conv2d_result_cache_name);
}
} // if (!cached_result_loaded)
uint32_t tensor_D_hash = TensorHash(tensor_D_computed.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
passed = (tensor_D_hash == cached_test_result.D);
EXPECT_EQ(tensor_D_hash, cached_test_result.D)
<< "Hash-based comparison failed for key:" << "\n" << cached_test_key << "\n";
}
else {
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
}
EXPECT_TRUE(passed);
@@ -345,12 +414,12 @@ public:
<< (split_k_mode == cutlass::conv::SplitKMode::kSerial ? "serial_reduction_" : "parallel_reduction_")
<< (Conv2d::kConvolutionalOperator == cutlass::conv::Operator::kFprop ? "fprop_" :
(Conv2d::kConvolutionalOperator == cutlass::conv::Operator::kDgrad ? "dgrad_" : "wgrad_"))
<< "nhwc_"
<< "ncxhwx_"
<< problem_size.N << "x"
<< problem_size.H << "x"
<< problem_size.W << "x"
<< problem_size.C
<< "_krsc_"
<< "_cxrskx_"
<< problem_size.K << "x"
<< problem_size.R << "x"
<< problem_size.S << "x"
@@ -381,10 +450,18 @@ public:
results
<< "\nA:\n" << tensor_A.host_view() << "\n"
<< "\nB:\n" << tensor_B.host_view() << "\n"
<< "\nB_reordered =\n" << tensor_B_reordered.host_view() << "\n"
<< "\nC:\n" << tensor_C.host_view() << "\n"
<< "\nD reference:\n" << tensor_D_reference.host_view() << "\n"
<< "\nD computed:\n" << tensor_D_computed.host_view() << "\n";
<< "\nC:\n" << tensor_C.host_view() << "\n";
results << "\nD reference (hash: " << cached_test_result.D << ")\n";
if (!cached_result_loaded) {
results
<< tensor_D_reference.host_view() << "\n";
}
results
<< "\nD computed (hash: " << tensor_D_hash << ")\n"
<< tensor_D_computed.host_view() << "\n";
}

View File

@@ -47,6 +47,8 @@
#include "cutlass/core_io.h"
#include "cutlass/util/tensor_view_io.h"
#include "cache_testbed_output.h"
namespace test {
namespace conv {
namespace device {
@@ -109,11 +111,17 @@ public:
scope = 2;
}
else if (bits == 16) {
scope = 3;
if (cutlass::sizeof_bits<ElementAccumulator>::value <= 16) {
scope = 3;
}
else {
scope = 5;
}
}
else {
scope = 8;
}
cutlass::reference::host::TensorFillRandomUniform(
view, seed, scope, -scope, 0);
}
@@ -263,6 +271,50 @@ public:
tensor_D_computed.sync_host();
//
// Reference check - support caching results
//
CachedTestKey cached_test_key = CreateCachedConv2dWithBroadcastTestKey<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute
>(
kConvolutionalOperator,
problem_size,
alpha,
beta,
tensor_A.host_view(),
tensor_B.host_view(),
tensor_C.host_view()
);
//
// Look for the cached key
//
bool cached_result_loaded = false;
CachedTestResult cached_test_result;
std::string conv2d_result_cache_name =
std::string("cached_results_") + CUTLASS_TARGET_NAME + ".txt";
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
CachedTestResultListing cached_results(conv2d_result_cache_name);
auto cached = cached_results.find(cached_test_key);
cached_result_loaded = cached.first;
if (cached_result_loaded) {
cached_test_result = cached.second;
}
}
if (!cached_result_loaded) {
#if CUTLASS_CONV_TEST_UNIT_REFERENCE_DEVICE_ENABLED
cutlass::reference::device::Conv2d<
@@ -309,9 +361,33 @@ public:
beta);
#endif
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
cached_test_result.D = TensorHash(tensor_D_reference.host_view());
CachedTestResultListing cached_results(conv2d_result_cache_name);
cached_results.append(cached_test_key, cached_test_result);
cached_results.write(conv2d_result_cache_name);
}
} // if (!cached_result_loaded)
uint32_t tensor_D_hash = TensorHash(tensor_D_computed.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
passed = (tensor_D_hash == cached_test_result.D);
EXPECT_EQ(tensor_D_hash, cached_test_result.D)
<< "Hash-based comparison failed for key:" << "\n" << cached_test_key << "\n";
}
else {
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
}
EXPECT_TRUE(passed);

View File

@@ -47,6 +47,8 @@
#include "cutlass/core_io.h"
#include "cutlass/util/tensor_view_io.h"
#include "cache_testbed_output.h"
namespace test {
namespace conv {
namespace device {
@@ -114,7 +116,12 @@ public:
scope = 2;
}
else if (bits == 16) {
scope = 3;
if (cutlass::sizeof_bits<ElementAccumulator>::value <= 16) {
scope = 3;
}
else {
scope = 5;
}
}
else {
scope = 8;
@@ -280,6 +287,50 @@ public:
tensor_D_computed.sync_host();
//
// Reference check - support caching results
//
CachedTestKey cached_test_key = CreateCachedConv2dWithReductionTestKey<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute
>(
kConvolutionalOperator,
problem_size,
alpha,
beta,
tensor_A.host_view(),
tensor_B.host_view(),
tensor_C.host_view()
);
//
// Look for the cached key
//
bool cached_result_loaded = false;
CachedTestResult cached_test_result;
std::string conv2d_result_cache_name =
std::string("cached_results_") + CUTLASS_TARGET_NAME + ".txt";
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
CachedTestResultListing cached_results(conv2d_result_cache_name);
auto cached = cached_results.find(cached_test_key);
cached_result_loaded = cached.first;
if (cached_result_loaded) {
cached_test_result = cached.second;
}
}
if (!cached_result_loaded) {
#if CUTLASS_CONV_TEST_UNIT_REFERENCE_DEVICE_ENABLED
cutlass::reference::device::Conv2d<
@@ -326,9 +377,33 @@ public:
beta);
#endif
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
cached_test_result.D = TensorHash(tensor_D_reference.host_view());
CachedTestResultListing cached_results(conv2d_result_cache_name);
cached_results.append(cached_test_key, cached_test_result);
cached_results.write(conv2d_result_cache_name);
}
} // if (!cached_result_loaded)
uint32_t tensor_D_hash = TensorHash(tensor_D_computed.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
passed = (tensor_D_hash == cached_test_result.D);
EXPECT_EQ(tensor_D_hash, cached_test_result.D)
<< "Hash-based comparison failed for key:" << "\n" << cached_test_key << "\n";
}
else {
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
}
EXPECT_TRUE(passed);

View File

@@ -67,7 +67,8 @@ TEST(SM80_Device_Conv3d_Dgrad_Analytic_ImplicitGemm_tf32ndhwc_tf32ndhwc_f32ndhwc
>,
cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,
3,
cutlass::arch::OpMultiplyAdd
cutlass::arch::OpMultiplyAdd,
cutlass::conv::IteratorAlgorithm::kAnalytic
>::Kernel;
using Conv3dDgrad = cutlass::conv::device::ImplicitGemmConvolution<Conv3dDgradKernel>;

View File

@@ -47,6 +47,8 @@
#include "conv3d_problems.h"
#include "cutlass/core_io.h"
#include "cache_testbed_output.h"
namespace test {
namespace conv {
namespace device {
@@ -321,6 +323,50 @@ public:
tensor_D_computed.sync_host();
//
// Reference check - support caching results
//
CachedTestKey cached_test_key = CreateCachedConv3dTestKey<
ElementA, LayoutA,
ElementB, LayoutB,
ElementC, LayoutC,
ElementAccumulator,
ElementCompute
>(
kConvolutionalOperator,
problem_size,
alpha,
beta,
tensor_A.host_view(),
tensor_B.host_view(),
tensor_C.host_view()
);
//
// Look for the cached key
//
bool cached_result_loaded = false;
CachedTestResult cached_test_result;
std::string conv2d_result_cache_name =
std::string("cached_results_") + CUTLASS_TARGET_NAME + ".txt";
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
CachedTestResultListing cached_results(conv2d_result_cache_name);
auto cached = cached_results.find(cached_test_key);
cached_result_loaded = cached.first;
if (cached_result_loaded) {
cached_test_result = cached.second;
}
}
if (!cached_result_loaded) {
#if CUTLASS_CONV_TEST_UNIT_REFERENCE_DEVICE_ENABLED
cutlass::reference::device::Conv3d<
@@ -368,10 +414,32 @@ public:
);
#endif
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
cached_test_result.D = TensorHash(tensor_D_reference.host_view());
CachedTestResultListing cached_results(conv2d_result_cache_name);
cached_results.append(cached_test_key, cached_test_result);
cached_results.write(conv2d_result_cache_name);
}
} // if (!cached_result_loaded)
uint32_t tensor_D_hash = TensorHash(tensor_D_computed.host_view());
if (CUTLASS_TEST_ENABLE_CACHED_RESULTS) {
passed = (tensor_D_hash == cached_test_result.D);
EXPECT_EQ(tensor_D_hash, cached_test_result.D)
<< "Hash-based comparison failed for key:" << "\n" << cached_test_key << "\n";
}
else {
passed = cutlass::reference::host::TensorEquals(
tensor_D_computed.host_view(),
tensor_D_reference.host_view());
}
EXPECT_TRUE(passed);
if (!passed) {
@@ -422,9 +490,19 @@ public:
results
<< "\nA:\n" << tensor_A.host_view() << "\n"
<< "\nB:\n" << tensor_B.host_view() << "\n"
<< "\nC:\n" << tensor_C.host_view() << "\n"
<< "\nD reference:\n" << tensor_D_reference.host_view() << "\n"
<< "\nD computed:\n" << tensor_D_computed.host_view() << "\n";
<< "\nC:\n" << tensor_C.host_view() << "\n";
results << "\nD reference (hash: " << cached_test_result.D << ")\n";
if (!cached_result_loaded) {
results
<< tensor_D_reference.host_view() << "\n";
}
results
<< "\nD computed (hash: " << tensor_D_hash << ")\n"
<< tensor_D_computed.host_view() << "\n";
}