diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0606491..7eed9ce2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,13 @@
# NVIDIA CUTLASS Changelog
+## [1.2.0](https://github.com/NVIDIA/cutlass/releases/tag/v1.2.0) (2018-10-26)
+ * Parallelized reductions across threadblocks ("Split-K")
+ * Improved IGEMM performance
+ * Batched strided WMMA GEMMs
-## 1.1.0 (2018-09-19)
+## [1.1.0](https://github.com/NVIDIA/cutlass/releases/tag/v1.1.0) (2018-09-19)
* Turing Features
- * WMMA GEMM targeting TensorCores - INT8, INT4, 1-bit
+ * WMMA GEMM targeting TensorCores - INT8, INT4, INT1
* Batched Strided GEMM
* Threadblock rasterization strategies
* Improved performance for adverse problem sizes and data layouts
@@ -16,13 +20,13 @@
* Examples
* Basic GEMM, tensor views, CUTLASS utilities, batched GEMM, WMMA GEMM
-## 1.0.1 (2018-06-11)
+## [1.0.1](https://github.com/NVIDIA/cutlass/releases/tag/v1.0.1) (2018-06-11)
* Intra-threadblock reduction added for small threadblock tile sizes
* sgemm_64x128x16, sgemm_128x128x16, sgemm_128x64x16, sgemm_128x32x16, sgemm_64x64x16, sgemm_64x32x16
* igemm_32x32x128
* GEMM _K_ residue handled during prologue prior to mainloop
- * Replaced Google Test copy with submodule. Use `git submodule init`
+ * Replaced Google Test copy with submodule. Use `git submodule init --recursive --update`
## [1.0.0](https://github.com/NVIDIA/cutlass/commit/2028ebe120aab22bfd0b2baf8902d4c9627eb33f) (2018-05-16)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fdd51ae8..2ec8cd7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -141,6 +141,10 @@ else()
string(APPEND NVCC_FLAGS " -lineinfo")
endif()
+if (UNIX)
+ string(APPEND NVCC_FLAGS " -Xcompiler -Wconversion")
+endif()
+
string(APPEND NVCC_FLAGS_DEBUG " -g")
string(APPEND NVCC_FLAGS_RELWITHDEBINFO " -O3")
string(APPEND NVCC_FLAGS_RELEASE " -O3")
@@ -169,6 +173,8 @@ file(GLOB CUTLASS_GEMM RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/gemm/*.h)
file(GLOB CUTLASS_UTIL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/util/*.h)
file(GLOB CUTLASS_DEVICE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/device/*.h)
file(GLOB CUTLASS_CORE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/*.h)
+file(GLOB CUTLASS_REDUCTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/reduction/*.h )
+
###################################################################################################
#
# Define build targets
@@ -178,6 +184,7 @@ file(GLOB CUTLASS_CORE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} cutlass/*.h)
source_group("cutlass\\gemm" FILES ${CUTLASS_GEMM})
source_group("cutlass\\util" FILES ${CUTLASS_UTIL})
source_group("cutlass\\device" FILES ${CUTLASS_DEVICE})
+source_group("cutlass\\reduction" FILES ${CUTLASS_REDUCTION})
source_group("cutlass" FILES ${CUTLASS_CORE})
add_library(CUTLASS INTERFACE)
@@ -187,6 +194,7 @@ target_sources(CUTLASS INTERFACE
${CUTLASS_UTIL}
${CUTLASS_DEVICE}
${CUTLASS_CORE}
+ ${CUTLASS_REDUCTION}
)
target_include_directories(CUTLASS INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -197,6 +205,7 @@ add_custom_target(cutlass_ide SOURCES
${CUTLASS_UTIL}
${CUTLASS_DEVICE}
${CUTLASS_CORE}
+ ${CUTLASS_REDUCTION}
)
# Doxygen is available. Generate documentation
if (DOXYGEN_FOUND)
diff --git a/CUTLASS.md b/CUTLASS.md
index 404f2d83..eb9e25e5 100644
--- a/CUTLASS.md
+++ b/CUTLASS.md
@@ -9,6 +9,7 @@ CUTLASS core components, and to identify their role in implementing GEMM computa
2. [General Matrix Multiply](#S-general-matrix-multiply)
3. [Core Components](#S-core-components)
4. [Utilities](#S-utilities)
+5. [Optimization Strategies](#S-optimization-strategies)
# 1. Design Patterns
@@ -26,7 +27,7 @@ objectives. This section is intended to provide more detail.
## Sequencing and Nesting of Collective Primitives
-CUTLASS embodies a design paradigm exemplified by the [CUB library](https://nvlabs.github.io/cub/) for expressing collective operations. Objects expose an interface for a problem that is then decomposed into concurrent subtasks executed by cooperating threadblocks, warps, and threads. For example, a grid-level object may be constructed with base pointers to the start of a GEMM operation, add a threadblock-dependent offset to partition the problem, and then compute a per-threadblock GEMM. This in turn performs some operations as a collection of cooperating threads, while it may partition other parts of the task into warp-level subtasks.
+CUTLASS embodies a design paradigm exemplified by the [CUB library](https://nvlabs.github.io/cub/) for expressing collective operations. Objects expose an interface for a problem that is then decomposed into concurrent subtasks executed by cooperating threadblocks, warps, and threads. For example, a grid-level object may be constructed with base pointers to the start of a GEMM operation, add a threadblock-dependent offset to partition the problem, and then compute a per-threadblock GEMM. This in turn performs some operations as a collection of cooperating threads, while it may partition other parts of the task into warp-level subtasks.
## Tiles and Iterators
@@ -48,7 +49,7 @@ CUTLASS can take advantage of this CUDA grid-invariant property by constructing
The design pattern in CUTLASS is for classes with nontrivial constructors to define `struct Params` as an inner class which contains grid-invariant state. These should define a constructor and an `initialize()` method. The `Params` structure should also include a data member corresponding to each data member in the parent class, so these too can be properly constructed in host code. The parent class should define a constructor which accepts `Params const &` as its first argument.
-For example, `cutlass::gemm::Gemm<>` should define `struct cutlass::gemm::Gemm::Params`. The latter should define data members for each data member in `cutlass::gemm::Gemm<>`.
+For example, `cutlass::gemm::Gemm<>` should define `struct cutlass::gemm::Gemm::Params`. The latter should define data members for each data member in `cutlass::gemm::Gemm<>`.
## Composable shared memory allocation
@@ -94,7 +95,7 @@ multiply operation performed by each iteration of the mainloop is referred to as
The threadblock loads a sequence of tiles from global memory and stores this data to shared memory. The iterative
access and traversal of tiles in global memory are performed by a _TileLoadIterator_, and storing to a circular
-buffer in shared memory is performed by a _GlobalLoadIterator_.
+buffer in shared memory is performed by a _GlobalLoadIterator_.
**[Global Load Stream](cutlass/gemm/gemm_global_stream.h)** manages loading of the threadblock-scope multiplicands to the GEMM kernel. It owns an iterator into global memory for loading tiles of data, a TensorAllocation in shared memory to hold the resulting tile, and an iterator for writing the tile into this allocation. A transformer exists to optionally transform the data as it is loaded which may of use to perform type conversion or, in the case of int8 GEMM, transpose 4x4 tiles held in registers.
@@ -109,24 +110,24 @@ The Global Load Stream template contains members defined by the following templa
The threadblock's _OutputTile_ is partitioned among the warps, and each computes a warp-level matrix product.
Data is loaded from shared memory into registers, and math instructions are dispatched to CUDA Cores or Tensor Cores.
-[**Shared Load Stream**](cutlass/gemm/gemm_shared_stream.h) manages loading of warp-level multiplicands from shared memory into registers. This owns an iterator for fetching data and the destination fragments for holding the results.
+[**Shared Load Stream**](cutlass/gemm/gemm_shared_stream.h) manages loading of warp-level multiplicands from shared memory into registers. This owns an iterator for fetching data and the destination fragments for holding the results.
* [GemmSharedLoadTile{A,B}](cutlass/gemm/gemm_shared_tile.h)
-**Matrix Multiply** computes a matrix product operation on data held in registers. Specializations exist for thread-level instructions such as single-precision fused multiply-add as well as warp-level matrix operations targeting TensorCores.
+**Matrix Multiply** computes a matrix product operation on data held in registers. Specializations exist for thread-level instructions such as single-precision fused multiply-add as well as warp-level matrix operations targeting TensorCores.
* [WMMA Multiply Add](cutlass/gemm/wmma_gemm_multiply_add.h)
## Thread-level GEMM
SGEMM, IGEMM, HGEMM, and DGEMM are computed by SIMT math instructions issued by thread-level matrix multiply
-procedures.
+procedures.
* [ThreadMultiplyAdd](cutlass/gemm/thread_multiply_add.h)
* [IGEMM specialization](cutlass/gemm/igemm_multiply_add.h)
* [HGEMM specialization](cutlass/gemm/hgemm_multiply_add.h)
-## Epilogue
+## Epilogue
The [**epilogue**](cutlass/gemm/gemm_epilogue.h) iteratively selects a subset of accumulator elements held by a warp, writes them to shared memory, and loads them by different threads such that a threadblock-scoped tile store operation will make contiguous, striped accesses to global memory. Thus, the flow of data utilizes the following components:
@@ -227,7 +228,7 @@ must specify compile-time constant tile sizes.
## Tile Structure
Tiled structures express an arrangement of data in memory as well as a logical mapping of concurrent CUDA
-threads to the problem space. For example, the CUTLASS GEMM
+threads to the problem space. For example, the CUTLASS GEMM
Tiled structures can be defined using the `cutlass::TileTraits<>` concept which defines the following
members. Collectively, these members offer a flexible way to define a 4-D subpartition of an integer
@@ -286,7 +287,7 @@ the next item in sequence.
To offer a generic solution that spans numerous data types and layouts, CUTLASS defines the _TileIterator_ concept.
-This concept provides access to a sequence of _tiles_ embedded in a tensor in addressable memory.
+This concept provides access to a sequence of _tiles_ embedded in a tensor in addressable memory.
The canonical CUTLASS tile iterator template is defined in [cutlass/tile_iterator.h](cutlass/tile_iterator.h).
@@ -296,9 +297,9 @@ A fragment is analogous to `std::array<>` in that it is a constant-sized array o
## Predicate Vector
-SIMT architectures utilize predicated execution in place of control flow when conditional code sequences are fairly short, on the order of a few machine instructions. While CUDA C++ does not include constructs at the language level for predication, PTX makes this explicit, and compilation to SASS is assumed to aggressively utilize predication. Typical applications are to initialize a sequence of bits used to mask memory operations and use these bits as predicates guarding memory load and store instructions.
+SIMT architectures utilize predicated execution in place of control flow when conditional code sequences are fairly short, on the order of a few machine instructions. While CUDA C++ does not include constructs at the language level for predication, PTX makes this explicit, and compilation to SASS is assumed to aggressively utilize predication. Typical applications are to initialize a sequence of bits used to mask memory operations and use these bits as predicates guarding memory load and store instructions.
-CUTLASS provides `PredicateVector` defined in [cutlass/predicate_vector.h](cutlass/predicate_vector.h) to manage a statically-sized bit vector, store them into general purpose registers, and efficiently access them in sequence. By storing four predicates per byte in hardware registers, the CUDA compiler is able to issue specialized instructions to achieve very efficient unpacking.
+CUTLASS provides `PredicateVector` defined in [cutlass/predicate_vector.h](cutlass/predicate_vector.h) to manage a statically-sized bit vector, store them into general purpose registers, and efficiently access them in sequence. By storing four predicates per byte in hardware registers, the CUDA compiler is able to issue specialized instructions to achieve very efficient unpacking.
# 4. Utilities
@@ -310,6 +311,46 @@ framework offering features such as:
* Components for allocating and initializing [host-side and device-side tensors](tools/util/host_tensor.h) usable by CUTLASS
* Reference implementations of [GEMM](tools/util/reference/host/gemm.h) and [element-wise operations](tools/util/reference/host/tensor_elementwise.h)
+
+# 5. Optimization Strategies
+
+This section describes several strategies taken to increase performance beyond what is achievable with
+a basic implementation of the hierarchical GEMM structure.
+
+
+## Threadblock Rasterization
+
+To maximize reuse of data held in the last level cache, CUTLASS defines several functions to
+affect the mapping of threadblocks to logical partitions of the GEMM problem. These map
+consecutively launched threadblocks to packed two-dimensional regions of the partitioned GEMM
+problem to increase the probability that these will access the same tiles of global memory at
+approximately the same time.
+
+Several functions are defined in [cutlass/gemm/threadblock_swizzle.h](cutlass/gemm/threadblock_swizzle.h).
+
+
+## Parallel Reductions across GEMM _K_
+
+Matrix product computations expose parallelism among _O(MN)_ independent inner product
+computations. For sufficiently large problem sizes, a GEMM kernel in CUTLASS may approach
+the theoretical maximum computational throughput. For small problems, however, there are
+too few threadblocks to efficiently occupy the entire GPU.
+
+As a recourse, parallelizing the reduction performed during the inner product computation
+enables more threadblocks to execute concurrently while still taking advantage of the throughput
+benefits of large threadblock-level GEMM tiles.
+
+CUTLASS implements parallel reductions across threadblocks by partitioning the GEMM _K_ dimension
+and launching an additional set of threadblocks for each partition. Consequently, we refer to
+this strategy within CUTLASS as "parallel reduction splitK." The "parallel reduction splitK" in cutlass requires the execution of 2 kernels. The first one is called partitionedK GEMM. The second one is called batched reduction.
+
+The partitionedK GEMM is very similar to one flavor of batched strided GEMM. Instead of requiring users to specify the problem size of each batch, partitionedK GEMM asks for the overall problem size and the number of partition that will be applied along K dimension for operand A and B. For example, parameters of m=128, n=128, k=4096 and partition=16 will result in 16 batched strided GEMMs with each batch of m=128, n=128, k=256. PartitionedK also allows scenario where k is not divisible by partition count. For example, parameters of m=128, n=128, k=4096 and partition=20 will result in 20 batched strided GEMMs with the first 19 batches of m=128, n=128, k=4096/20=204 and the last batch of m=128, n=128, k=220.
+
+The batched reduction kernel will further perform reduction along the K-dimension. Thus, the input of the batched reduction kernel is the output (C) of partitionedK GEMM. An workspace memory is managed by the users to store this intermediate results.
+
+An example of splitK usage can be found [here](examples/06_splitK_gemm/splitK_gemm.cu).
+
+
# Copyright
Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
@@ -335,4 +376,3 @@ Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
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.
```
-
diff --git a/README.md b/README.md
index 19d30f3a..c57a7c65 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@

-# CUTLASS 1.1
+# CUTLASS 1.2
-_CUTLASS 1.1.0 - September 2018_
+_CUTLASS 1.2.0 - October 2018_
-CUTLASS 1.1 is a collection of CUDA C++ template abstractions for implementing
+CUTLASS is a collection of CUDA C++ template abstractions for implementing
high-performance matrix-multiplication (GEMM) at all levels and scales within CUDA.
It incorporates strategies for hierarchical decomposition and data movement similar
to those used to implement cuBLAS. CUTLASS decomposes these "moving parts" into
@@ -22,12 +22,19 @@ point (FP64) types. Furthermore, CUTLASS demonstrates CUDA's WMMA API for targe
the programmable, high-throughput _Tensor Cores_ provided by NVIDIA's Volta architecture
and beyond.
-CUTLASS 1.1 is described in the [CUTLASS Documentation](CUTLASS.md) and the accompanying
+CUTLASS 1.2 is described in the [CUTLASS Documentation](CUTLASS.md) and the accompanying
[Doxygen documentation](https://nvidia.github.io/cutlass).
We describe the structure of an efficient GEMM in our talk at the
[GPU Technology Conference 2018](http://on-demand.gputechconf.com/gtc/2018/presentation/s8854-cutlass-software-primitives-for-dense-linear-algebra-at-all-levels-and-scales-within-cuda.pdf).
+# What's New in CUTLASS 1.2
+_October 2018_
+* [Parallelized Reductions](CUTLASS.md#parallel-reductions-across-gemm-k)
+* Batched strided WMMA GEMM
+
+
# What's New in CUTLASS 1.1
+_September 2018_
* [CUTLASS Documentation](CUTLASS.md)
* [Examples](examples/)
diff --git a/cutlass/coord.h b/cutlass/coord.h
index 625a2272..e90af8a1 100644
--- a/cutlass/coord.h
+++ b/cutlass/coord.h
@@ -313,6 +313,56 @@ struct Coord {
////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Scalar multiplication
+template
+CUTLASS_HOST_DEVICE
+Coord operator*(T s, Coord coord) {
+ CUTLASS_PRAGMA_UNROLL
+ for (int i = 0; i < Rank; ++i) {
+ coord[i] *= s;
+ }
+ return coord;
+}
+
+/// Scalar multiplication
+template
+CUTLASS_HOST_DEVICE
+Coord operator*(Coord coord, T s) {
+ CUTLASS_PRAGMA_UNROLL
+ for (int i = 0; i < Rank; ++i) {
+ coord[i] *= s;
+ }
+ return coord;
+}
+
+/// Scalar division
+template
+CUTLASS_HOST_DEVICE
+Coord operator/(T s, Coord coord) {
+ CUTLASS_PRAGMA_UNROLL
+ for (int i = 0; i < Rank; ++i) {
+ coord[i] = s / coord[i];
+ }
+ return coord;
+}
+
+/// Scalar division
+template
+CUTLASS_HOST_DEVICE
+Coord operator/(Coord coord, T s) {
+ CUTLASS_PRAGMA_UNROLL
+ for (int i = 0; i < Rank; ++i) {
+ coord[i] /= s;
+ }
+ return coord;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Integer-valued make_Coord
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
/// Helper to make a 2-element coordinate
CUTLASS_HOST_DEVICE
Coord<1> make_Coord(int _0) {
diff --git a/cutlass/cutlass.h b/cutlass/cutlass.h
index 15ea83c0..2851a5f0 100644
--- a/cutlass/cutlass.h
+++ b/cutlass/cutlass.h
@@ -32,7 +32,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
#define CUTLASS_MAJOR 1
-#define CUTLASS_MINOR 1
+#define CUTLASS_MINOR 2
#define CUTLASS_PATCH 0
#define CUTLASS_VERSION ((CUTLASS_MAJOR)*100 + (CUTLASS_MINOR)*10 + CUTLASS_PATCH)
@@ -49,21 +49,7 @@
#define CUTLASS_ASSERT(x) assert(x)
-// CUTLASS_PRAGMA_(UNROLL|NO_UNROLL) optimization directives for the CUDA compiler.
-#if defined(__CUDA_ARCH__)
-#if defined(_MSC_VER)
-#define CUTLASS_PRAGMA_UNROLL __pragma("unroll")
-#define CUTLASS_PRAGMA_NO_UNROLL __pragma("unroll 1")
-#else
-#define CUTLASS_PRAGMA_UNROLL _Pragma("unroll")
-#define CUTLASS_PRAGMA_NO_UNROLL _Pragma("unroll 1")
-#endif
-#else
-#define CUTLASS_PRAGMA_UNROLL
-#define CUTLASS_PRAGMA_NO_UNROLL
-#endif
-
-#define CUTLASS_GEMM_LOOP CUTLASS_PRAGMA_NO_UNROLL
+#include "cutlass/util/performance_tuning.h"
// A small helper class to dump a type at compile time
// Usage:: DumpType::Class
diff --git a/cutlass/gemm/device_gemm.h b/cutlass/gemm/device_gemm.h
new file mode 100644
index 00000000..aaf4bfe7
--- /dev/null
+++ b/cutlass/gemm/device_gemm.h
@@ -0,0 +1,67 @@
+/***************************************************************************************************
+* 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 device level GEMM implemented by more than one kernels.
+*/
+#pragma once
+
+#if !defined(__CUDACC_RTC__)
+#include
+#endif
+
+#include "cutlass/coord.h"
+#include "cutlass/util/platform.h"
+namespace cutlass {
+namespace gemm {
+
+template
+struct DeviceGemm {
+ /// The Traits
+ typedef DeviceGemmTraits_ Traits;
+ /// Use the params object defined in traits
+ typedef typename Traits::Params Params;
+
+ /// Support for NVRTC
+#if !defined(__CUDACC_RTC__)
+ /// Launch the kernels in order
+ static __host__ cudaError_t launch(Params const& params) {
+ Traits::GemmTraits::KernelClass::launch(params.GemmParams);
+ cudaError_t err = cudaGetLastError();
+ if (err != cudaSuccess)
+ return err;
+ Traits::ReductionTraits::KernelClass::launch(params.ReductionParams);
+ return cudaGetLastError();
+ }
+#endif
+
+ ///
+ /// Methods
+ ///
+
+ /// Ctor.
+ CUTLASS_DEVICE DeviceGemm() {}
+};
+} // namespace device_gemm
+} // namespace cutalss
diff --git a/cutlass/gemm/device_gemm_traits.h b/cutlass/gemm/device_gemm_traits.h
new file mode 100644
index 00000000..fbcfef3e
--- /dev/null
+++ b/cutlass/gemm/device_gemm_traits.h
@@ -0,0 +1,170 @@
+/***************************************************************************************************
+* 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
+#include "cutlass/gemm/device_gemm.h"
+#include "cutlass/matrix_traits.h"
+#include "cutlass/gemm/gemm_desc.h"
+#include "tools/util/type_traits.h"
+#include
+
+namespace cutlass {
+namespace gemm {
+
+template <
+ /// The Tratis for the first kernel
+ typename GemmTraits_,
+ /// The Traits for the second kernel
+ typename ReductionTraits_
+>
+struct SplitkPIGemmTraits {
+ typedef GemmTraits_ GemmTraits;
+ typedef ReductionTraits_ ReductionTraits;
+ typedef SplitkPIGemmTraits This_;
+ typedef typename cutlass::gemm::DeviceGemm KernelClass;
+
+ ///
+ typedef typename GemmTraits::Index Index;
+ ///
+ typedef typename ReductionTraits::ScalarAlphaBeta Scalar;
+ ///
+ typedef typename GemmTraits::ScalarA ScalarA;
+ ///
+ typedef typename GemmTraits::ScalarB ScalarB;
+ ///
+ typedef typename GemmTraits::ScalarD ScalarAccum;
+ ///
+ typedef typename ReductionTraits::ScalarC ScalarC;
+ ///
+ typedef typename ReductionTraits::ScalarD ScalarD;
+ /// The layout of A. can be deduced from the layout set in batched gemm
+ static MatrixLayout::Kind const kLayoutA = GemmTraits::kLayoutA;
+ /// The layout of B. can be deduced from the layout set in batched gemm
+ static MatrixLayout::Kind const kLayoutB = GemmTraits::kLayoutB;
+
+ struct Params {
+ /// The dimensions of the GEMM in K, N, M order
+ GemmCoord problem_size;
+
+ /// Check if params are init
+ bool problem_size_initialized;
+ /// The pointer to workspace memory
+ ScalarAccum *workspace_ptr;
+ ///
+ int workspace_size;
+ /// The Params for the first kernel
+ typename GemmTraits::Params GemmParams;
+ /// The Params for the second kernel
+ typename ReductionTraits::Params ReductionParams;
+
+ /// ctor
+ Params() :
+ workspace_size(0),
+ problem_size_initialized(false) {}
+ /// ctor
+ Params(Index m_,
+ Index n_,
+ Index k_
+ ):
+ problem_size(k_, n_, m_, 1),
+ workspace_size(0),
+ problem_size_initialized(true) {
+
+ }
+
+ /// init problem is needed if using default ctor
+ void init_problem(Index m_,
+ Index n_,
+ Index k_){
+ problem_size = GemmCoord(k_, n_, m_, 1);
+ problem_size_initialized = true;
+ }
+
+ int initialize(Scalar alpha_,
+ ScalarA const* d_a_,
+ Index lda_,
+ ScalarB const* d_b_,
+ Index ldb_,
+ Scalar beta_,
+ ScalarC const* d_c_,
+ Index ldc_,
+ ScalarD* d_d_,
+ Index ldd_,
+ ScalarAccum *workspace_ptr_) {
+
+ workspace_ptr = workspace_ptr_;
+
+ //call GemmTraits (first kernel) param
+ //for the first kernel A is A, B is B, C and D are workspace
+ //alpha is one, beta is zero, partitionK_count is reductionTraits::reductionSize
+ typename cutlass::gemm::GemmDesc
+ desc(
+ problem_size,
+ typename cutlass::TypeTraits::host_type(1.0f), /*alpha*/
+ TensorRef(d_a_, lda_),
+ TensorRef(d_b_, ldb_),
+ typename cutlass::TypeTraits::host_type(0.0f), /*beta*/
+ TensorRef(workspace_ptr, problem_size.m()), /*m = ldc, workspace is not transposed and is packed*/
+ TensorRef(workspace_ptr, problem_size.m()) /*m = ldd, workspace is not transposed and is packed*/
+ );
+ GemmParams.initialize(desc, ReductionTraits::ReductionSize);
+
+
+ //call batched reduction (second kernel) param
+ ReductionParams.initialize(problem_size.m(), /*m*/
+ problem_size.n(), /*n*/
+ alpha_, /*alpha*/
+ beta_, /*beta*/
+ problem_size.n() * problem_size.m() /*reduction_stride*/,
+ workspace_ptr,
+ problem_size.m(),
+ d_c_,
+ ldc_,
+ d_d_,
+ ldd_);
+
+ return 0;
+ }
+
+ // workspace will be used to store D (output) from the first gemm kernel (not D of the entire gemm)
+ // note typedef typename GemmTraits::ScalarD ScalarAccum;
+ // workspace of size of M * N * Reduction
+ int required_workspace_memory_in_byte(){
+ assert(problem_size_initialized == true);
+ workspace_size = problem_size.n() * problem_size.m() * ReductionTraits::ReductionSize * static_cast(sizeof(ScalarAccum));
+ return workspace_size;
+ }
+
+
+ };
+
+};
+
+} // namespace device_gemm
+} // namespace cutalss
diff --git a/cutlass/gemm/gemm.h b/cutlass/gemm/gemm.h
index 6340ab4f..3aec7928 100644
--- a/cutlass/gemm/gemm.h
+++ b/cutlass/gemm/gemm.h
@@ -243,23 +243,27 @@ struct Gemm {
// We may want to use shared memory to clear the registers.
typedef typename Traits::ClearAccumulators ClearAccumulators;
+ // Get the bounds for each thread, it maybe different than problem_size
+ Coord<3> bounds = block_swizzle.get_threadblock_bounds(params.problem_size,
+ params.partitionK_range);
+
// The streams to read A/B from global memory to shared memory.
typename Traits::GlobalLoadStream global_to_shared_stream(
params.global_to_shared_stream,
shared_storage.main_loop.global_to_shared_stream,
shared_storage.main_loop.threadblock_tile.reference(),
- params.problem_size.knm(),
+ bounds,
threadblock_offset);
// update A and B pointer offset based on batch_id and batch_stride_offset
- //global_to_shared_stream.add_pointer_offset(block_swizzle.get_batch_id(), params.batch_stride_A, params.batch_stride_B);
- global_to_shared_stream += make_Coord(block_swizzle.get_batch_id(), 0, 0);
+ global_to_shared_stream.add_batch_offset(block_swizzle.get_batch_id());
// Create the accumulator clear.
ClearAccumulators clear;
// Deal with residue in prolog.
- global_to_shared_stream.move_to_residue(params.problem_size[0], Traits::OutputTile::kD);
+ // global_to_shared_stream.move_to_residue(params.problem_size[0], Traits::OutputTile::kD);
+ global_to_shared_stream.move_to_residue(bounds[0], Traits::OutputTile::kD);
// Fetch the fragments for A and B from global memory.
global_to_shared_stream.copy();
@@ -271,7 +275,8 @@ struct Gemm {
Traits::shared_store_fence(false);
// Rollback to the beginning of the first tile (if residue exists).
- global_to_shared_stream.rollback(params.problem_size[0] % Traits::OutputTile::kD);
+ // global_to_shared_stream.rollback(params.problem_size[0] % Traits::OutputTile::kD);
+ global_to_shared_stream.rollback(bounds[0] % Traits::OutputTile::kD);
// The stream of data from shared memory to fragments.
typename Traits::SharedStream shared_load_stream(
@@ -288,18 +293,17 @@ struct Gemm {
clear.clear(accumulators);
// Initial index
- Index outer_k = params.problem_size[0] - Traits::OutputTile::kD;
-
+ // Index outer_k = params.problem_size[0] - Traits::OutputTile::kD;
+ // problem_size[0] might be bigger than bounds[0]
+ Index outer_k = bounds[0] - Traits::OutputTile::kD;
// Check if we are computing residue in prolog or not.
if (Traits::GemmConfig::kResidueInProlog) {
-
// Execute all mainloop iterations but the last one.
CUTLASS_GEMM_LOOP
for (; outer_k > 0; outer_k -= Traits::OutputTile::kD) {
consume_tile(
global_to_shared_stream, shared_load_stream, accumulators, outer_k);
-
}
// Don't load data for the last "residue" portion since we've already computed the residue.
@@ -307,7 +311,6 @@ struct Gemm {
for (; outer_k > -Traits::OutputTile::kD; outer_k -= Traits::OutputTile::kD) {
consume_tile(
global_to_shared_stream, shared_load_stream, accumulators, outer_k);
-
}
} else {
// When kResidueSeparate = true, execute all mainloop iterations but the last two without any
@@ -319,17 +322,14 @@ struct Gemm {
for (; outer_k > Traits::OutputTile::kD; outer_k -= Traits::OutputTile::kD) {
consume_tile(
global_to_shared_stream, shared_load_stream, accumulators, outer_k);
-
}
}
// Execute remaining tiles with K-residue predicate updates enabled.
-
CUTLASS_GEMM_LOOP
for (; outer_k > -Traits::OutputTile::kD; outer_k -= Traits::OutputTile::kD) {
consume_tile(
global_to_shared_stream, shared_load_stream, accumulators, outer_k);
-
}
}
diff --git a/cutlass/gemm/gemm_coord.h b/cutlass/gemm/gemm_coord.h
index 8e36bb04..e029af35 100644
--- a/cutlass/gemm/gemm_coord.h
+++ b/cutlass/gemm/gemm_coord.h
@@ -127,6 +127,12 @@ struct GemmCoord : public Coord<4, int> {
Coord<2> nm() const {
return make_Coord(n(), m());
}
+
+ /// Obtains a Coord<2> from GemmCoord
+ CUTLASS_HOST_DEVICE
+ Coord<2> mn() const {
+ return make_Coord(m(), n());
+ }
/// Obtains a Coord<2> from GemmCoord
CUTLASS_HOST_DEVICE
diff --git a/cutlass/gemm/gemm_epilogue.h b/cutlass/gemm/gemm_epilogue.h
index d9469bb5..0e0cfc53 100644
--- a/cutlass/gemm/gemm_epilogue.h
+++ b/cutlass/gemm/gemm_epilogue.h
@@ -131,20 +131,19 @@ struct GemmEpilogue {
params.iterator_c, problem_size, block, pointer_offset, predicate_offset);
// update C pointer offset based on batch_id and batch_stride_offset
- //global_load_iterator.add_pointer_offset(batch_id * params.batch_stride_offset_c);
- global_load_iterator += make_Coord(batch_id, 0, 0);
+ global_load_iterator.add_pointer_offset(batch_id * params.batch_stride_C);
// The transformer for C.
GlobalTransformerC transformer_c;
// The transformer for D.
GlobalTransformerD transformer_d;
+
// The iterator to store into the D matrix.
GlobalStoreIteratorD global_store_iterator(
params.iterator_d, problem_size, block, pointer_offset, predicate_offset);
// update D pointer offset based on batch_id and batch_stride_offset
- //global_store_iterator.add_pointer_offset(batch_id * params.batch_stride_offset_d);
- global_store_iterator += make_Coord(batch_id, 0, 0);
+ global_store_iterator.add_pointer_offset(batch_id * params.batch_stride_D);
SharedStoreTransformerD shared_store_transformer;
typename SharedStoreTransformerD::OutputFragment shared_store_transformed_d;
@@ -171,6 +170,7 @@ struct GemmEpilogue {
int const offset = (h * Iterations::kW + w) * SharedStoreIteratorD::Fragment::kElements;
shared_store_transformer.transform(accumulators, offset, shared_store_transformed_d);
+
shared_store_iterator.store_post_increment(shared_store_transformed_d);
// Make sure the data is in shared memory.
@@ -182,7 +182,6 @@ struct GemmEpilogue {
// Do the math.
typename GlobalTransformerD::InputFragment fragment_d;
-
if (kSourceRequired) {
// Transform C fragment.
transformer_c.transform(fragment_c, transformed_c);
diff --git a/cutlass/gemm/gemm_epilogue_traits.h b/cutlass/gemm/gemm_epilogue_traits.h
index c6aff71e..bffd5e51 100644
--- a/cutlass/gemm/gemm_epilogue_traits.h
+++ b/cutlass/gemm/gemm_epilogue_traits.h
@@ -97,6 +97,8 @@ struct GemmEpilogueTraits {
typedef Functor_ Functor;
/// The index.
typedef Index_ Index;
+ /// The long index
+ typedef long long LongIndex;
/// We do not support 3D or 4D shapes.
static_assert(Iterations::kD == 1 && Iterations::kC == 1, "Unsupported 3D/4D shapes");
@@ -114,8 +116,16 @@ struct GemmEpilogueTraits {
Index stride_h, stride_w;
/// The params for the C iterator.
typename GlobalLoadIteratorC::Params iterator_c;
+
+ /// Batch stride for C matrix
+ LongIndex batch_stride_C;
+
/// The params for the D global iterator.
typename GlobalStoreIteratorD::Params iterator_d;
+
+ /// Batch stride for C matrix
+ LongIndex batch_stride_D;
+
/// The params for the D shared store iterator.
typename SharedStoreIteratorD::Params shared_store_iterator_d;
/// The params for the D shared load stream.
@@ -139,22 +149,29 @@ struct GemmEpilogueTraits {
this->stride_w = 0;
// Setup the params for the global memory iterator for C.
error_code = iterator_c.initialize(desc.C.data(),
- desc.batch_stride_C,
+ desc.C.leading_dim(),
desc.C.leading_dim(),
desc.problem_size[1],
stride_w,
Delta::kW);
+
+ batch_stride_C = desc.batch_stride_C;
+
if (error_code) {
return error_code;
}
// Setup the params for the global memory iterator for D.
- return iterator_d.initialize(desc.D.data(),
- desc.batch_stride_D,
+ error_code = iterator_d.initialize(desc.D.data(),
+ desc.D.leading_dim(),
desc.D.leading_dim(),
desc.problem_size[1],
stride_w,
Delta::kW);
+
+ batch_stride_D = desc.batch_stride_D;
+
+ return error_code;
}
};
diff --git a/cutlass/gemm/gemm_global_stream.h b/cutlass/gemm/gemm_global_stream.h
index 6ea72cf3..1ae2963c 100644
--- a/cutlass/gemm/gemm_global_stream.h
+++ b/cutlass/gemm/gemm_global_stream.h
@@ -80,6 +80,8 @@ struct GlobalLoadStream {
typedef typename LoadIterator::Pointer Pointer;
/// The index.
typedef typename LoadIterator::Index Index;
+ /// The index.
+ typedef typename LoadIterator::LongIndex LongIndex;
/// The tile
typedef typename LoadIterator::Tile Tile;
@@ -94,24 +96,46 @@ struct GlobalLoadStream {
struct Params {
// The load iterator.
typename LoadIterator::Params load_iterator;
+
+ /// Batch stride in global memory
+ LongIndex batch_stride;
+
// The store iterator.
typename StoreIterator::Params store_iterator;
+
// Offset to residue.
Index offset_to_residue;
+ // Offset to residue for the last partition
+ Index offset_to_residue_last_partition;
+
/// Setup the params.
CUTLASS_HOST_DEVICE int initialize(Pointer pointer,
- long long batch_stride,
+ LongIndex batch_stride_,
Index ldm,
- Index _offset_to_residue) {
+ Index offset_to_residue_,
+ Index offset_to_residue_last_partition_) {
- offset_to_residue = _offset_to_residue;
- int error_code = load_iterator.initialize(pointer, batch_stride, ldm);
+ int error_code = load_iterator.initialize(pointer, ldm, ldm);
if (error_code) {
return error_code;
}
+
+ batch_stride = batch_stride_;
+ offset_to_residue = offset_to_residue_;
+ offset_to_residue_last_partition = offset_to_residue_last_partition_;
+
return store_iterator.initialize();
}
+
+ CUTLASS_DEVICE Index get_offset_to_residue() {
+ if (blockIdx.z == gridDim.z - 1) { //last partition
+ return offset_to_residue_last_partition;
+ }
+ else {
+ return offset_to_residue;
+ }
+ }
};
/// Contains private storage in shared memory needed by the objects within this class. Note,
@@ -124,7 +148,7 @@ struct GlobalLoadStream {
//
/// Maps a coordinate in the GEMM's (K, N, M) coordinate system to global memory
- CUTLASS_DEVICE static Coord<3> project_coordinate(Coord<3> const& coord, Index d_offset = 0) {
+ CUTLASS_HOST_DEVICE static Coord<3> project_coordinate(Coord<3> const& coord, Index d_offset = 0) {
bool const kKstrided =
GemmMultiplicandTraits::kKstrided;
Coord<3> tile_coord = ProjectOperand::project(coord);
@@ -140,21 +164,20 @@ struct GlobalLoadStream {
Coord<3> const bounds,
Coord<3> const& _threadblock_offset)
: params(_params),
- multiplicand_bounds(project_coordinate(bounds, 1)),
threadblock_offset(project_coordinate(_threadblock_offset)),
- load_iterator(params.load_iterator,
- project_coordinate(bounds, 1), /*multiplicant_bounds*/
- project_coordinate(_threadblock_offset) /*threablock_offset*/),
+ multiplicand_bounds(project_coordinate(bounds, 1)),
+ load_iterator(params.load_iterator, threadblock_offset),
transformer(),
- store_iterator(params.store_iterator, threadblock_tile_ref.data())
- {
+ store_iterator(params.store_iterator, threadblock_tile_ref.data()) {
load_iterator.initialize_predicates(multiplicand_bounds, threadblock_offset);
fetched_fragment.clear();
}
/// Load the data from shared memory to the fetch fragment.
- CUTLASS_DEVICE void copy() { load_iterator.load_post_increment(fetched_fragment); }
+ CUTLASS_DEVICE void copy() {
+ load_iterator.load_post_increment(fetched_fragment);
+ }
/// Commit the data.
CUTLASS_DEVICE void commit() {
@@ -176,8 +199,9 @@ struct GlobalLoadStream {
Index kResidue = k % kTileK;
if (kResidue) {
residue(kResidue);
+ Index this_offset_residue = params.get_offset_to_residue();
+ load_iterator.add_pointer_offset(this_offset_residue * load_iterator.stride_advance());
}
- load_iterator.add_pointer_offset(params.offset_to_residue * load_iterator.stride_advance());
}
/// Rollback to the beginning of the first tile
@@ -187,9 +211,9 @@ struct GlobalLoadStream {
int const kBlock = kOperand == GemmOperand::kA
? (kLayout == MatrixLayout::kColumnMajor ? Tile::kH : Tile::kW)
: (kLayout == MatrixLayout::kRowMajor ? Tile::kH : Tile::kW);
-
- load_iterator.add_pointer_offset(-(params.offset_to_residue + kBlock) *
- load_iterator.stride_advance());
+ Index this_offset_residue = params.get_offset_to_residue();
+ load_iterator.add_pointer_offset(-(this_offset_residue + kBlock) *
+ load_iterator.stride_advance());
}
/// Adds a Coord<3> to the underlying global load iterator
@@ -198,16 +222,22 @@ struct GlobalLoadStream {
return *this;
}
+ /// Adds an offset based on batch stride
+ CUTLASS_DEVICE GlobalLoadStream &add_batch_offset(int batch_id) {
+ load_iterator.add_pointer_offset(batch_id * params.batch_stride);
+ return *this;
+ }
+
//
// Data members
//
/// Parameters
Params params;
- /// Multiplicand bounds
- Coord<3> multiplicand_bounds;
/// Threadblock offset
Coord<3> threadblock_offset;
+ /// Multiplicand bounds
+ Coord<3> multiplicand_bounds;
/// The iterator.
LoadIterator load_iterator;
/// The fragment to fetch from shared memory.
diff --git a/cutlass/gemm/gemm_global_tile.h b/cutlass/gemm/gemm_global_tile.h
index a355ebea..5174ce67 100644
--- a/cutlass/gemm/gemm_global_tile.h
+++ b/cutlass/gemm/gemm_global_tile.h
@@ -188,6 +188,8 @@ struct GemmGlobalIteratorAb
typedef typename TileTraits_::Threads Threads;
/// The index.
typedef Index_ Index;
+ /// Long index
+ typedef long long LongIndex;
/// The thread offset
typedef typename TileTraits_::ThreadOffset ThreadOffset;
/// Specifies in which dimension post-increment accesses advance.
@@ -201,35 +203,9 @@ struct GemmGlobalIteratorAb
struct Params : public BaseParams {
/// Initializes params to load a strip-mined tile, given pointer and stride_h.
CUTLASS_HOST_DEVICE int initialize(Scalar const* ptr,
- long long stride_d,
+ Index stride_d,
Index stride_h) {
- Index inc_d = 0;
- Index inc_advance = 0;
- // Move by some columns for each iteration in the H dimension.
- Index inc_h = Base::Delta::kH * stride_h;
-
- // Move by some more columns in the number of iterations if the D dimension is > 1.
- if (Base::Delta::kD > 0) {
- inc_d = Base::Delta::kD * stride_h - (Base::Iterations::kH - 1) * inc_h;
- }
-
- // Move to the beginning of the next iteration.
- if (kAdvance == IteratorAdvance::kH && Base::Delta::kD > 0) {
- inc_advance = inc_d;
- } else if (kAdvance == IteratorAdvance::kH) {
- inc_advance = inc_h;
- } else if (Base::Delta::kD > 0) {
- inc_advance = (Base::Iterations::kW + 0) * ShapeCount::kWc -
- (Base::Iterations::kH - 1) * inc_h -
- (Base::Iterations::kD - 1) * Base::Delta::kD * stride_h;
- } else {
- inc_advance = (Base::Iterations::kW + 0) * ShapeCount::kWc -
- (Base::Iterations::kH - 1) * inc_h;
- }
-
- Base::Params::initialize(
- ptr, stride_d, stride_h, 1, inc_d, inc_h, 0, inc_advance);
- return 0;
+ return BaseParams::initialize(ptr, stride_d, stride_h, kAdvance == IteratorAdvance::kH ? 0 : 1);
}
};
@@ -268,7 +244,6 @@ struct GemmGlobalIteratorAb
/// Ctor.
CUTLASS_HOST_DEVICE GemmGlobalIteratorAb(Params const& _params,
- const Coord<3>& bounds,
const Coord<3>& threadblock_offset,
ThreadOffset thread_offset_func = ThreadOffset())
: params(_params) {
@@ -304,11 +279,6 @@ struct GemmGlobalIteratorAb
/// That's the residue! Update the predicates.
CUTLASS_HOST_DEVICE void residue(Index k) {
- // The coordinates of the thread.
- Index block_h = thread_offset[1];
- // The contiguous dimension.
- Index block_w = thread_offset[2];
-
// Update the predicate vector.
for (int d = 0; d < Base::Iterations::kD; ++d) {
for (int h = 0; h < Base::Iterations::kH; ++h) {
@@ -316,9 +286,9 @@ struct GemmGlobalIteratorAb
for (int c = 0; c < Base::Iterations::kC; ++c) {
Index offset = 0;
if (kAdvance == IteratorAdvance::kH) {
- offset += block_h + h * Base::Delta::kH + d * Base::Delta::kD;
+ offset += thread_offset[1] + h * Base::Delta::kH + d * Base::Delta::kD;
} else {
- offset += block_w + w * Base::Delta::kW;
+ offset += thread_offset[2] + w * Base::Delta::kW;
}
int const bit = ComputeOffsetFromShape::get(d, h, w, c);
@@ -340,7 +310,7 @@ struct GemmGlobalIteratorAb
/// Adds a vector offset to the iterator
CUTLASS_HOST_DEVICE GemmGlobalIteratorAb & operator+=(Coord<3> const &offset) {
- long long _offset = offset.template dot(
+ LongIndex _offset = offset.template dot(
make_Coord(params.stride_d, params.stride_h, params.stride_w)
);
@@ -419,6 +389,8 @@ struct GemmGlobalIteratorCd : public TileIteratorBasepointer = pointer;
// Stride per batch
- stride_d = batch_stride;
+ stride_d = stride_d_;
// Each column of the matrix.
stride_h = TileTraits_::ThreadsDelta::kH * ldm;
// Each thread output 1 column per iteration. The stride between columns is given by the
@@ -463,6 +435,21 @@ struct GemmGlobalIteratorCd : public TileIteratorBasepointer = pointer;
+ stride_d = _stride_d;
+ stride_h = _stride_h;
+ inc_advance = _inc_advance;
+ inc_h = _inc_h;
+ predicate_inc_advance = _predicate_inc_advance;
+ predicate_inc_h = _predicate_inc_h;
+ predicate_offset = _predicate_offset;
+
+ return 0;
+ }
};
/// Parameters.
@@ -471,20 +458,7 @@ struct GemmGlobalIteratorCd : public TileIteratorBase thread_offset;
/// The predicates for the row.
cutlass::PredicateVector predicates;
-
- /// Ctor.
- CUTLASS_HOST_DEVICE GemmGlobalIteratorCd(Params const& _params,
- const Coord<3>& bounds,
- const Coord<3>& block_offset,
- ThreadOffset thread_offset_func = ThreadOffset())
- : params(_params) {
- thread_offset = thread_offset_func();
- // Prepare the vector of predicates.
- for (int i = 0; i < Base::Iterations::kW; ++i) {
- predicates.set(i, thread_offset[2] + i * Base::Delta::kW < bounds[2]);
- }
- }
-
+
/// Ctor.
CUTLASS_HOST_DEVICE GemmGlobalIteratorCd(Params const& _params,
const Coord<3>& bounds,
@@ -527,7 +501,7 @@ struct GemmGlobalIteratorCd : public TileIteratorBase const &offset) {
- long long _offset = offset.template dot(
+ LongIndex _offset = offset.template dot(
make_Coord(params.stride_d, params.stride_h, 1)
);
params.pointer += _offset;
@@ -568,7 +542,7 @@ struct GemmGlobalIteratorCd : public TileIteratorBase
diff --git a/cutlass/gemm/gemm_shared_stream.h b/cutlass/gemm/gemm_shared_stream.h
index df20bd6c..ed158d6b 100644
--- a/cutlass/gemm/gemm_shared_stream.h
+++ b/cutlass/gemm/gemm_shared_stream.h
@@ -92,7 +92,9 @@ struct SharedLoadStream {
}
/// Load the data from shared memory to the fetch fragment.
- CUTLASS_DEVICE void copy() { iterator.load_post_increment(fetched[0]); }
+ CUTLASS_DEVICE void copy() {
+ iterator.load_post_increment(fetched[0]);
+ }
/// Load the data from shared memory to the fetch fragment.
CUTLASS_DEVICE void copy(int step) { iterator.load(fetched[step % 2], step); }
diff --git a/cutlass/gemm/gemm_stream_pair.h b/cutlass/gemm/gemm_stream_pair.h
index 0a6df15e..f1c22edf 100644
--- a/cutlass/gemm/gemm_stream_pair.h
+++ b/cutlass/gemm/gemm_stream_pair.h
@@ -111,7 +111,7 @@ struct GlobalLoadStreamPair {
CUTLASS_DEVICE GlobalLoadStreamPair(Params const ¶ms,
SharedStorage &shared_storage,
ThreadblockTileRef const &threadblock_tile_ref,
- Coord<3> const &bounds,
+ Coord<3> const bounds,
Coord<3> const &block_offset = make_Coord(0, 0, 0))
: stream_a(params.stream_a,
shared_storage.stream_a,
@@ -131,6 +131,13 @@ struct GlobalLoadStreamPair {
return *this;
}
+ CUTLASS_DEVICE
+ GlobalLoadStreamPair & add_batch_offset(int batch_id) {
+ stream_a.add_batch_offset(batch_id);
+ stream_b.add_batch_offset(batch_id);
+ return *this;
+ }
+
/// Trigger the copies from shared memory to registers.
CUTLASS_DEVICE void copy() {
stream_a.copy();
diff --git a/cutlass/gemm/gemm_traits.h b/cutlass/gemm/gemm_traits.h
index fd6efb46..b588de0a 100644
--- a/cutlass/gemm/gemm_traits.h
+++ b/cutlass/gemm/gemm_traits.h
@@ -418,6 +418,9 @@ struct GemmTraits {
/// GEMM problem size
GemmCoord problem_size;
+ /// The K range for every partition except the last one
+ int partitionK_range;
+
/// Parameters object for the global load stream
typename GlobalLoadStream::Params global_to_shared_stream;
@@ -433,6 +436,8 @@ struct GemmTraits {
// Set the problem size.
problem_size = desc.problem_size;
+ // there is no partitionK in the default case
+ partitionK_range = problem_size[0];
// Compute grid dimensions
BlockSwizzle block_swizzle;
this->block = dim3(GemmConfig::kThreads);
@@ -441,15 +446,18 @@ struct GemmTraits {
make_Coord_from_shape());
// Compute offset to residue.
+ // partitionK_range <= problem_size[0]
Index gemm_k = problem_size[0];
- Index offset_to_residue = (gemm_k % OutputTile::kD) ? gemm_k - (gemm_k % OutputTile::kD) : 0;
+ Index offset_to_residue_last_partition = (gemm_k % OutputTile::kD) ? gemm_k - (gemm_k % OutputTile::kD) : 0;
+ Index offset_to_residue = (partitionK_range % OutputTile::kD) ? partitionK_range - (partitionK_range % OutputTile::kD) : 0;
// Initialize parameters objects for
int error_code = global_to_shared_stream.stream_a.initialize(
desc.A.data(),
desc.batch_stride_A,
desc.A.leading_dim(),
- offset_to_residue
+ offset_to_residue,
+ offset_to_residue_last_partition
);
if (error_code) {
return error_code;
@@ -459,7 +467,8 @@ struct GemmTraits {
desc.B.data(),
desc.batch_stride_B,
desc.B.leading_dim(),
- offset_to_residue
+ offset_to_residue,
+ offset_to_residue_last_partition
);
if (error_code) {
@@ -516,7 +525,6 @@ struct GemmTraits {
Index ldd,
long long int batch_stride_D,
Index batch_count) {
-
GemmDesc desc(
GemmCoord(k, n, m, batch_count),
alpha,
@@ -533,6 +541,121 @@ struct GemmTraits {
return this->initialize(desc);
}
+
+ /// Helper to construct a partitionedK GEMM params
+ template
+ CUTLASS_HOST_DEVICE int initialize(GemmDesc_ const& partitonK_desc, Index partitionK_count_) {
+ // partitionK GEMM is a specialized batched stried gemm with different K ranges per batch
+ // the problem_size of each batch is (lastK_size, n, m)
+ // add more comments here
+ // the k range for every batch excpet the last one
+ //assert(partitionK_count_ > 0);
+ partitionK_range = partitonK_desc.problem_size.k() / partitionK_count_;
+ // the k range of the last batch
+ // int lastK_range = (partitonK_desc.problem_size.k() % partitionK_range) + partitionK_range;
+ int lastK_range = partitonK_desc.problem_size.k() - partitionK_range * (partitionK_count_ - 1);
+ int k_size = lastK_range;
+ int lda = partitonK_desc.A.stride(0);
+ int ldb = partitonK_desc.B.stride(0);
+ int ldc = partitonK_desc.C.stride(0);
+ int ldd = partitonK_desc.D.stride(0);
+ int n = partitonK_desc.problem_size.n();
+
+
+ long long int batch_stride_A = (kLayoutA == cutlass::MatrixLayout::kColumnMajor) ? lda * partitionK_range : partitionK_range;
+ long long int batch_stride_B = (kLayoutB == cutlass::MatrixLayout::kColumnMajor) ? partitionK_range : partitionK_range * ldb;
+ long long int batch_stride_C = ldc * n;
+ long long int batch_stride_D = ldd * n;
+
+ GemmDesc desc(
+ //we pass lastK_size as per batch K. there is also a range that will match partitionK_size
+ GemmCoord(k_size, partitonK_desc.problem_size.n(), partitonK_desc.problem_size.m(), partitionK_count_),
+ partitonK_desc.alpha,
+ partitonK_desc.A,
+ batch_stride_A,
+ partitonK_desc.B,
+ batch_stride_B,
+ partitonK_desc.beta,
+ partitonK_desc.C,
+ batch_stride_C,
+ partitonK_desc.D,
+ batch_stride_D
+ );
+
+ // Set the problem size.
+ problem_size = desc.problem_size;
+
+ // Compute grid dimensions
+ BlockSwizzle block_swizzle;
+ this->block = dim3(GemmConfig::kThreads);
+ this->grid = block_swizzle.get_grid_layout(
+ problem_size,
+ make_Coord_from_shape());
+
+ // Compute offset to residue.
+ // partitionK_range <= problem_size[0]
+ Index gemm_k = problem_size[0];
+ Index offset_to_residue_last_partition = (gemm_k % OutputTile::kD) ? gemm_k - (gemm_k % OutputTile::kD) : 0;
+ Index offset_to_residue = (partitionK_range % OutputTile::kD) ? partitionK_range - (partitionK_range % OutputTile::kD) : 0;
+
+ // Initialize parameters objects for
+ int error_code = global_to_shared_stream.stream_a.initialize(
+ desc.A.data(),
+ desc.batch_stride_A,
+ desc.A.leading_dim(),
+ offset_to_residue,
+ offset_to_residue_last_partition
+ );
+ if (error_code) {
+ return error_code;
+ }
+
+ error_code = global_to_shared_stream.stream_b.initialize(
+ desc.B.data(),
+ desc.batch_stride_B,
+ desc.B.leading_dim(),
+ offset_to_residue,
+ offset_to_residue_last_partition
+ );
+
+ if (error_code) {
+ return error_code;
+ }
+
+ // The epilogue.
+ return epilogue.initialize(desc);
+ }
+
+
+ /// Helper to construct a partitionedK GEMM params
+ CUTLASS_HOST_DEVICE int initialize(Index m,
+ Index n,
+ Index k,
+ typename Epilogue::Scalar alpha,
+ ScalarA const* d_a,
+ Index lda,
+ ScalarB const* d_b,
+ Index ldb,
+ typename Epilogue::Scalar beta,
+ ScalarC const* d_c,
+ Index ldc,
+ ScalarD* d_d,
+ Index ldd,
+ Index partitionK_count_) {
+
+ GemmDesc desc(
+ GemmCoord(k, n, m, 1),
+ alpha,
+ TensorRef(d_a, lda),
+ TensorRef(d_b, ldb),
+ beta,
+ TensorRef(d_c, ldc),
+ TensorRef(d_d, ldd)
+ );
+
+
+ return this->initialize(desc, partitionK_count_);
+ }
};
// The storage for the main loop + prologue.
diff --git a/cutlass/gemm/igemm_global_tile.h b/cutlass/gemm/igemm_global_tile.h
index 7a9c1573..845678a8 100644
--- a/cutlass/gemm/igemm_global_tile.h
+++ b/cutlass/gemm/igemm_global_tile.h
@@ -100,10 +100,13 @@ struct IgemmGlobalIteratorAb : public GemmGlobalIteratorAb
/// Constructor.
CUTLASS_DEVICE IgemmGlobalIteratorAb(typename Base::Params const& _params,
- const Coord<3>& bounds,
const Coord<3>& threadblock_offset,
ThreadOffset thread_offset_func = ThreadOffset())
- : Base(_params, bounds, threadblock_offset, thread_offset_func), mask_(0xffffffff) {
+ : Base(_params, threadblock_offset, thread_offset_func), mask_(0xffffffff) { }
+
+ CUTLASS_DEVICE void initialize_predicates(const Coord<3>& bounds, const Coord<3>& threadblock_offset) {
+
+ Base::initialize_predicates(bounds, threadblock_offset);
// The number of elements read in a single iteration.
int const kBlock = TileTraits_::Tile::kW;
// The residue.
diff --git a/cutlass/gemm/igemm_multiply_add.h b/cutlass/gemm/igemm_multiply_add.h
index 5ff6c7c1..2b09cba2 100644
--- a/cutlass/gemm/igemm_multiply_add.h
+++ b/cutlass/gemm/igemm_multiply_add.h
@@ -71,6 +71,8 @@ struct ThreadMultiplyAdd
FragmentB const& b,
Accumulators const& c,
Accumulators& d) {
+
+ #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 610)
// The inputs.
int const* a_int = reinterpret_cast(&a[0]);
int const* b_int = reinterpret_cast(&b[0]);
@@ -82,6 +84,7 @@ struct ThreadMultiplyAdd
: "r"(a_int[i]), "r"(b_int[j]), "r"(c[j * AccumulatorsPerThread::kW + i]));
}
}
+ #endif
}
};
diff --git a/cutlass/gemm/threadblock_swizzle.h b/cutlass/gemm/threadblock_swizzle.h
index fe7a3be7..eab8595a 100644
--- a/cutlass/gemm/threadblock_swizzle.h
+++ b/cutlass/gemm/threadblock_swizzle.h
@@ -80,7 +80,7 @@ struct IdentityBlockSwizzle {
return grid;
}
- ///
+ ///get threadblock offset, without considering tha batch dim
CUTLASS_DEVICE Coord<3> get_threadblock_offset(Coord<3> const &OutputTile) {
dim3 block = swizzle();
Coord<3> threadblock_offset =
@@ -93,6 +93,26 @@ struct IdentityBlockSwizzle {
dim3 block = swizzle();
return block.z;
}
+
+ /// check if at the last partition
+ CUTLASS_DEVICE bool is_last_partition() {
+ if (get_batch_id() == (gridDim.z - 1))
+ return true;
+ else
+ return false;
+ }
+
+ ///
+ CUTLASS_DEVICE Coord<3> get_threadblock_bounds(GemmCoord const &problem_size,
+ int partitionK_range) {
+ // every partition except the last one has a smaller range
+ // partitionK_range is the bounds for every partition except the last one
+ // the last partition's bounds is the same with problem size
+ if(is_last_partition())
+ return problem_size.knm();
+ else
+ return make_Coord(partitionK_range, problem_size.n(), problem_size.m());
+ }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -226,6 +246,26 @@ struct ColumnMajorBlockSwizzle {
dim3 block = swizzle();
return block.z;
}
+
+ /// check if at the last partition
+ CUTLASS_DEVICE bool is_last_partition() {
+ if (get_batch_id() == (gridDim.z - 1))
+ return true;
+ else
+ return false;
+ }
+
+ ///
+ CUTLASS_DEVICE Coord<3> get_threadblock_bounds(GemmCoord const &problem_size,
+ int partitionK_range) {
+ // every partition except the last one has a smaller range
+ // partitionK_range is the bounds for every partition except the last one
+ // the last partition's bounds is the same with problem size
+ if (is_last_partition())
+ return problem_size.knm();
+ else
+ return make_Coord(partitionK_range, problem_size.n(), problem_size.m());
+ }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -379,6 +419,26 @@ struct RowMajorBlockSwizzle {
dim3 block = swizzle();
return block.z;
}
+
+ /// check if at the last partition
+ CUTLASS_DEVICE bool is_last_partition() {
+ if (get_batch_id() == (gridDim.z - 1) )
+ return true;
+ else
+ return false;
+ }
+
+ ///
+ CUTLASS_DEVICE Coord<3> get_threadblock_bounds(GemmCoord const &problem_size,
+ int partitionK_range) {
+ // every partition except the last one has a smaller range
+ // partitionK_range is the bounds for every partition except the last one
+ // the last partition's bounds is the same with problem size
+ if (is_last_partition())
+ return problem_size.knm();
+ else
+ return make_Coord(partitionK_range, problem_size.n(), problem_size.m());
+ }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/cutlass/gemm/wmma_gemm_epilogue_traits.h b/cutlass/gemm/wmma_gemm_epilogue_traits.h
index f35264dd..0eccab02 100644
--- a/cutlass/gemm/wmma_gemm_epilogue_traits.h
+++ b/cutlass/gemm/wmma_gemm_epilogue_traits.h
@@ -45,7 +45,7 @@ namespace gemm {
////////////////////////////////////////////////////////////////////////////////////////////////////
-template
+template
struct WmmaGemmEpilogueTraitsHelper {
/// The scalar.
typedef typename EpilogueFunctor_::Scalar Scalar;
@@ -104,7 +104,10 @@ struct WmmaGemmEpilogueTraitsHelper {
// The number of threads.
Shape<1, ShapeCount::kCount, GemmConfig_::kWarpSize>,
// The number of scalars per LDS.
- GemmConfig_::kScalarsPerLdsD>
+ GemmConfig_::kScalarsPerLdsD,
+ // this parameter helps with swizzling when accum is fp32 and output is fp16
+ sizeof(Accumulator_) / sizeof(typename GemmConfig_::ScalarD)
+ >
SharedLoadTileTraits;
/// The iterator to load D from shared memory.
diff --git a/cutlass/gemm/wmma_gemm_global_tile.h b/cutlass/gemm/wmma_gemm_global_tile.h
index ce369d0e..2c197a8b 100644
--- a/cutlass/gemm/wmma_gemm_global_tile.h
+++ b/cutlass/gemm/wmma_gemm_global_tile.h
@@ -103,18 +103,18 @@ struct WmmaGemmGlobalIteratorCd : public GemmGlobalIteratorCdpointer = pointer;
// Stride between GEMMs
- BaseParams::stride_d = batch_stride;
+ this->stride_d = batch_stride;
// Setup the base stride. One "group of threads" per column.
- BaseParams::stride_h = ldm;
+ this->stride_h = ldm;
// Each thread output 1 column per iteration. .
- BaseParams::inc_h = ldm * TileTraits_::Threads::kH;
- BaseParams::inc_advance = BaseParams::inc_h + epilogue_stride_w;
+ this->inc_h = ldm * TileTraits_::Threads::kH;
+ this->inc_advance = this->inc_h + epilogue_stride_w;
- BaseParams::predicate_offset = n;
- BaseParams::predicate_inc_h = TileTraits_::Threads::kH;
- BaseParams::predicate_inc_advance = BaseParams::predicate_inc_h + epilogue_delta_w;
+ this->predicate_offset = n;
+ this->predicate_inc_h = TileTraits_::Threads::kH;
+ this->predicate_inc_advance = this->predicate_inc_h + epilogue_delta_w;
return 0;
}
diff --git a/cutlass/gemm/wmma_gemm_shared_tile.h b/cutlass/gemm/wmma_gemm_shared_tile.h
index 1a90e2f1..1fa99bc8 100644
--- a/cutlass/gemm/wmma_gemm_shared_tile.h
+++ b/cutlass/gemm/wmma_gemm_shared_tile.h
@@ -173,6 +173,7 @@ struct WmmaGemmSharedStoreTileDTraits {
/// The strides in each dimension between different loads/stores.
typedef Shape<0, 0, Warps::kW * WmmaShape_::kW, 0> ImmediateOffsetStrides;
+
/// ThreadOffset
struct ThreadOffset {
CUTLASS_HOST_DEVICE
@@ -192,7 +193,7 @@ struct WmmaGemmSharedStoreTileDTraits {
////////////////////////////////////////////////////////////////////////////////////////////////////
-template
+template
struct WmmaGemmSharedLoadTileDTraits {
/// The scalar.
typedef Scalar_ Scalar;
@@ -201,7 +202,7 @@ struct WmmaGemmSharedLoadTileDTraits {
/// The access size
static int const kAccessSize = kScalarsPerLds_;
/// The tile.
- typedef typename ReshapeTile::Tile Tile;
+ typedef typename WmmaReshapeTile::Tile Tile;
/// The threads.
typedef typename ReshapeThreads::Threads Threads;
/// The threads strides.
@@ -212,12 +213,13 @@ struct WmmaGemmSharedLoadTileDTraits {
/// The strides in each dimension between different loads/stores.
typedef Shape<0, Threads::kH * ShapeCount::kWc, Threads::kW * kScalarsPerLds_> Delta;
/// The strides in each dimension between different loads/stores.
- typedef Shape<0, Threads::kH * ShapeCount::kWc, Threads::kW * kScalarsPerLds_>
+ typedef Shape<0, Threads::kH * ShapeCount::kWc, Threads::kW * kScalarsPerLds_, kScalarsPerLds_>
ImmediateOffsetStrides;
/// The number of iterations needed to load/store the tile.
typedef Shape<1, Tile::kH / Threads::kH, Tile::kW / Threads::kW, Tile::kC / kScalarsPerLds_>
Iterations;
+
/// ThreadOffset
struct ThreadOffset {
CUTLASS_HOST_DEVICE
diff --git a/cutlass/gemm/wmma_gemm_traits.h b/cutlass/gemm/wmma_gemm_traits.h
index 65ffb50b..f140b7ba 100644
--- a/cutlass/gemm/wmma_gemm_traits.h
+++ b/cutlass/gemm/wmma_gemm_traits.h
@@ -46,7 +46,7 @@ namespace gemm {
////////////////////////////////////////////////////////////////////////////////////////////////////
-template <
+ template <
/// The layout for A.
MatrixLayout::Kind kLayoutA_,
/// The layout for B.
@@ -68,7 +68,18 @@ template <
/// The number of scalars per LDG for A.
int kScalarsPerLdgA_,
/// The number of scalars per LDG for B.
- int kScalarsPerLdgB_>
+ int kScalarsPerLdgB_,
+ /// The number of scalars per LDS for A.
+ int KScalarsPerLdsA_,
+ /// The number of scalars per LDS for B.
+ int KscalarsPerLdsB_,
+ /// The number of scalars per LDG for C and STG for D.
+ int kScalarsPerLdgCAndStgD_,
+ /// The number of scalars per STS for D.
+ int kScalarsPerStsD_,
+ /// The number of scalars per LDS for D.
+ int kScalarsPerLdsD_
+>
struct WmmaGemmConfig : public GemmConfig<
/// The scalar type for A.
ScalarA_,
@@ -94,19 +105,19 @@ struct WmmaGemmConfig : public GemmConfig<
/// The number of scalars per STS for A.
kScalarsPerLdgA_,
/// The number of scalars per LDS for A.
- 8,
+ KScalarsPerLdsA_,
/// The number of scalars per LDG for B.
kScalarsPerLdgB_,
/// The number of scalars per STS for B.
kScalarsPerLdgB_,
/// The number of scalars per LDS for B.
- 8,
+ KscalarsPerLdsB_,
/// The number of scalars per LDG for C and STG for D.
- 16 / sizeof(ScalarC_),
+ kScalarsPerLdgCAndStgD_,
/// The number of scalars per STS for D.
- 16 / sizeof(Accumulator_),
+ kScalarsPerStsD_,
/// The number of scalars per LDS for D.
- 16 / sizeof(Accumulator_),
+ kScalarsPerLdsD_,
/// The number of stages in shared memory.
1,
/// If true, residue is computed in mainloop. If false, separate loops are instantiated.
@@ -955,6 +966,16 @@ template <
int kScalarsPerLdgA_,
/// The number of halfs loaded in one LDG for B.
int kScalarsPerLdgB_,
+ /// The number of scalars per LDS for A.
+ int KScalarsPerLdsA_,
+ /// The number of scalars per LDS for B.
+ int KscalarsPerLdsB_,
+ /// The number of scalars per LDG for C and STG for D.
+ int kScalarsPerLdgCAndStgD_,
+ /// The number of scalars per STS for D.
+ int kScalarsPerStsD_,
+ /// The number of scalars per LDS for D.
+ int kScalarsPerLdsD_,
/// The index.
typename Index_>
struct WmmaGemmTraitsHelper {
@@ -969,7 +990,13 @@ struct WmmaGemmTraitsHelper {
WarpGemmShape_,
InstructionShape_,
kScalarsPerLdgA_,
- kScalarsPerLdgB_>
+ kScalarsPerLdgB_,
+ KScalarsPerLdsA_,
+ KscalarsPerLdsB_,
+ kScalarsPerLdgCAndStgD_,
+ kScalarsPerStsD_,
+ kScalarsPerLdsD_
+ >
GemmConfig;
/// The GEMM config for A.
@@ -1042,7 +1069,7 @@ struct WmmaGemmTraitsHelper {
typedef ClearAccumulators ClearAccumulators;
/// The helper to create the epilogue traits.
- typedef WmmaGemmEpilogueTraitsHelper EpilogueTraitsHelper;
+ typedef WmmaGemmEpilogueTraitsHelper EpilogueTraitsHelper;
/// The traits class for the epilogue.
typedef SimplifiedGemmEpilogueTraits
GemmEpilogueTraits;
@@ -1084,6 +1111,16 @@ template <
int kScalarsPerLdgA_ = 8,
/// The number of scalars per LDG for B.
int kScalarsPerLdgB_ = 8,
+ /// The number of scalars per LDS for A.
+ int KScalarsPerLdsA_ = 8,
+ /// The number of scalars per LDS for B.
+ int KscalarsPerLdsB_ = 8,
+ /// The number of scalars per LDG for C and STG for D.
+ int kScalarsPerLdgCAndStgD_ = 16 / sizeof(ScalarC_),
+ /// The number of scalars per STS for D.
+ int kScalarsPerStsD_ = 16 / sizeof(Accumulator_),
+ /// The number of scalars per LDS for D.
+ int kScalarsPerLdsD_ = 16 / sizeof(Accumulator_),
/// The index.
typename Index_ = int,
/// The helper class.
@@ -1099,6 +1136,11 @@ template <
InstructionShape_,
kScalarsPerLdgA_,
kScalarsPerLdgB_,
+ KScalarsPerLdsA_,
+ KscalarsPerLdsB_,
+ kScalarsPerLdgCAndStgD_,
+ kScalarsPerStsD_,
+ kScalarsPerLdsD_,
Index_> >
struct WmmaGemmTraits : public GemmTraits<
// The config.
diff --git a/cutlass/matrix_traits.h b/cutlass/matrix_traits.h
index 08a43a99..044c0ed2 100644
--- a/cutlass/matrix_traits.h
+++ b/cutlass/matrix_traits.h
@@ -153,7 +153,7 @@ struct MatrixCoord : public Coord<2, int> {
//
// Coord stride = TensorRefMapFunc::stride(leading_dim);
//
-struct MatrixLayout {
+namespace MatrixLayout {
/// Enumeration defining fundamental contiguous layouts.
enum Kind { kRowMajor, kColumnMajor };
diff --git a/cutlass/reduction/batched_reduction.h b/cutlass/reduction/batched_reduction.h
new file mode 100644
index 00000000..28e14c49
--- /dev/null
+++ b/cutlass/reduction/batched_reduction.h
@@ -0,0 +1,175 @@
+/***************************************************************************************************
+* 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 Implements a software-pipelined efficient batched reduction.
+D = alpha * Reduction(A) + beta * C
+*/
+#pragma once
+
+#if !defined(__CUDACC_RTC__)
+#include
+#endif
+
+#include "cutlass/coord.h"
+#include "cutlass/util/platform.h"
+#include "cutlass/fragment.h"
+
+namespace cutlass {
+namespace reduction {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template
+__global__ __launch_bounds__(batched_reduction_::Traits::kThreads, 1) void batched_reduction_kernel(typename batched_reduction_::Params params) {
+ // Construct the batched_reduction object
+ batched_reduction_ batched_reduction(params);
+ batched_reduction.run();
+}
+
+template
+struct BatchedReduction {
+ /// This class
+ typedef BatchedReduction This_;
+ /// The traits
+ typedef BatchedReductionTraits_ Traits;
+ /// Params
+ typedef typename Traits::Params Params;
+ /// functor
+ typedef typename Traits::Functor Functor;
+
+ /// ctor
+ CUTLASS_DEVICE BatchedReduction(Params const ¶ms_)
+ : params(params_), functor(params_.functorParams) {}
+
+ /// main operation method
+ /// D = alpha * Reduction(A) + beta * C
+ CUTLASS_DEVICE void run() {
+#if (__CUDA_ARCH__ >= 600)
+ // Swizzle the IDs of the block
+ typename Traits::BlockSwizzle block_swizzle;
+ Coord<3> threadblock_offset =
+ block_swizzle.get_threadblock_offset(make_Coord_from_shape());
+
+ int subTileSize = gridDim.x * Traits::SubTile::kW;
+ int tileSize = params.problem_size[1] * params.problem_size[2];
+ int subTileOffset = threadblock_offset[2] + threadIdx.x * Traits::ThreadShape::kW;
+
+ int subTileBase = 0;
+
+ typename Traits::ScalarA inRegs[Traits::maxInReg];
+ typename Traits::ScalarAccum AccumRegs[Traits::maxOutReg];
+
+ for (int subTile = 0; subTile < tileSize; subTile += subTileSize) {
+ int tileOffset = subTileBase + subTileOffset;
+ // Init AccumRegs
+ for (int i = 0; i < Traits::ThreadShape::kW; i++)
+ AccumRegs[i] = static_cast(0.0f);
+ // Fetch c0
+ typename Traits::ScalarAccum c0[Traits::ThreadShape::kW];
+ for (int i = 0; i< Traits::ThreadShape::kW; i++)
+ c0[i] = static_cast(params.d_c[tileOffset + i]);
+
+ // Fetch partial sums from A
+#pragma unroll
+ for (int s = 0; s < Traits::ReductionSize; s++) {
+ int inRegOffset = s * Traits::ThreadShape::kW;
+ int dOffset = (s * tileSize) + tileOffset;
+#pragma unroll
+ for (int i = 0; i< Traits::ThreadShape::kW; i++) {
+ inRegs[inRegOffset + i] = params.d_a[dOffset + i];
+ }
+ }
+
+ // Accumulate
+#pragma unroll
+ for (int s = 0; s < Traits::ReductionSize; s++) {
+ int inRegOffset = s * Traits::ThreadShape::kW;
+#pragma unroll
+ for (int i = 0; i < Traits::ThreadShape::kW; i++) {
+ //AccumRegs[i] = cuFma(params.alpha, inRegs[inRegOffset + i], AccumRegs[i]);
+ //AccumRegs[i] = params.alpha * inRegs[inRegOffset + i] + AccumRegs[i];
+ AccumRegs[i] = static_cast(inRegs[inRegOffset + i]) + AccumRegs[i];
+ }
+ }
+ // calling functor
+ functor_caller(AccumRegs, c0, AccumRegs);
+
+ // Store AccumRegs to D
+#pragma unroll
+ for (int i = 0; i < Traits::ThreadShape::kW; i++) {
+ params.d_d[tileOffset + i] = static_cast(AccumRegs[i]);
+ }
+
+ // Advance sub-tile pointer
+ subTileBase += subTileSize;
+ } // end for loop
+#endif //#if (__CUDA_ARCH__ >= 600)
+ }
+
+ template
+ CUTLASS_DEVICE void functor_caller(typename Traits::ScalarAccum const *accum, typename Traits::ScalarAccum const *old, typename Traits::ScalarAccum *output) {
+ if (ThreadShapeMultiple2 == true) {
+ for (int i = 0; i < Traits::ThreadShape::kW / 2; i++) {
+ functor.template evaluate(&accum[2 * i], &old[2 * i], &output[2 * i]);
+ }
+ }
+ else {
+ for (int i = 0; i < Traits::ThreadShape::kW; i++) {
+ functor.template evaluate(&accum[i], &old[i], &output[i]);
+ }
+ }
+ }
+
+ //
+ // Static function members
+ //
+#if !defined(__CUDACC_RTC__)
+ /// Launch the kernel.
+ static __host__ cudaError_t launch(Params const& params,
+ cudaStream_t stream = cudaStreamDefault) {
+ // Setup the grid.
+ typename Traits::BlockSwizzle block_swizzle;
+ dim3 grid = block_swizzle.get_grid_layout(params.problem_size,
+ make_Coord_from_shape());
+
+ dim3 block;
+ block.x = Traits::kThreads;
+ batched_reduction_kernel<<>>(params);
+ return cudaGetLastError();
+ }
+#endif
+
+ //
+ // Data members
+ //
+
+ /// The params.
+ Params const& params;
+ // The functor.
+ Functor functor;
+};
+
+} // namespace reduction
+} // namespace cutlass
diff --git a/cutlass/reduction/batched_reduction_traits.h b/cutlass/reduction/batched_reduction_traits.h
new file mode 100644
index 00000000..bc0c1f2a
--- /dev/null
+++ b/cutlass/reduction/batched_reduction_traits.h
@@ -0,0 +1,192 @@
+/***************************************************************************************************
+* 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 structural properties of complete batched reduction.
+D = alpha * Reduction(A) + beta * C
+*/
+#pragma once
+#include "cutlass/cutlass.h"
+#include "cutlass/shape.h"
+#include "cutlass/reduction/threadblock_swizzle.h"
+#include "cutlass/reduction/batched_reduction.h"
+#include "cutlass/gemm/linear_scaling.h"
+
+namespace cutlass {
+namespace reduction {
+
+/*
+OutputTile defines the work load per thread block
+Subtile defines the work load per thread block per iteration
+OutputTile / Subtile = number of iterations within a kernel
+ThreadShape defines the work load per thread
+Subtile / ThreadShape = number of threads per thread block
+*/
+template <
+ /// The scalar type for A
+ typename ScalarA_,
+ /// The scalar type for C
+ typename ScalarC_,
+ /// The scalar type for D
+ typename ScalarD_,
+ /// the scalar type for alpha,
+ typename ScalarAlphaBeta_,
+ /// The scalar type for accumulator
+ typename ScalarAccum_,
+ /// Reduction work load per batch
+ int ReductionSize_ = 1,
+ /// The output tile, work load per thread block,
+ typename OutputTile_ = Shape<1, 1, 128>,
+ /// The subtile
+ typename SubTile_ = Shape<1, 1, 64>,
+ /// Work load per thread, per subtile
+ typename ThreadShape_ = Shape<1, 1, 2>,
+ /// The index
+ typename Index_ = int,
+ /// The block swizzle to reorganize the grid.
+ typename BlockSwizzle_ = DefaultBlockSwizzle,
+ /// The input register vector size in kernel
+ int maxInReg_ = 160,
+ /// The output register vector size in kernel
+ int maxOutReg_ = 64,
+ /// The functor that will be executed at the end
+ typename Functor_ = typename cutlass::gemm::LinearScaling >
+>
+struct BatchedReductionTraits {
+ ///
+ typedef BatchedReductionTraits This_;
+ /// The struct that consumes this Traits
+ typedef typename cutlass::reduction::BatchedReduction KernelClass;
+ ///
+ typedef OutputTile_ OutputTile;
+ ///
+ typedef SubTile_ SubTile;
+ ///
+ typedef ThreadShape_ ThreadShape;
+ /// The input pointer type
+ typedef ScalarA_ ScalarA;
+ ///
+ typedef ScalarC_ ScalarC;
+ /// The output pointer type
+ typedef ScalarD_ ScalarD;
+ /// The alpha beta type
+ typedef ScalarAlphaBeta_ ScalarAlphaBeta;
+ /// The type for accumulation
+ typedef ScalarAccum_ ScalarAccum;
+ /// The index
+ typedef Index_ Index;
+ /// The thread block swizzle
+ typedef BlockSwizzle_ BlockSwizzle;
+ ///
+ static const int ReductionSize = ReductionSize_;
+ /// check if threadShape is multiple of 2.
+ static const bool ThreadShapeMultiple2 = (ThreadShape::kW % 2 == 0);
+ ///
+ typedef Functor_ Functor;
+ /// Parameteres object constructable on the host
+ /// The number of threads per thread block. can be deduced
+ static int const kThreads = SubTile::kW / ThreadShape::kW;
+ //
+ static int const maxInReg = maxInReg_;
+ //
+ static int const maxOutReg = maxOutReg_;
+ //
+ static_assert(SubTile::kW % ThreadShape::kW == 0, "cannot evenly distribute work load among threads");
+ //
+ static_assert(kThreads % 32 == 0, "threads per threadblock is not multiple of 32");
+ //
+ static_assert(OutputTile::kW % SubTile::kW == 0, "cannot evenly distribute work load among iterations");
+ //
+ static_assert(ReductionSize * ThreadShape::kW <= maxInReg, "ReductionSize * ThreadShape::kW should not be bigger than maxInReg");
+ //
+ static_assert(ThreadShape::kW <= maxOutReg, "ThreadShape::kW should not be bigger than maxOutReg");
+
+ struct Params {
+ /// The dimension of output tensor
+ Coord<3> problem_size;
+ /// The alpha
+ ScalarAlphaBeta alpha;
+ /// The beta
+ ScalarAlphaBeta beta;
+ /// stride between two element that will be sumed
+ long long int reduction_stride;
+ //
+ ScalarA const *d_a;
+ //
+ Index lda;
+ //
+ ScalarC const *d_c;
+ //
+ Index ldc;
+ //
+ ScalarD *d_d;
+ //
+ Index ldd;
+ /// The functor params.
+ typename Functor::Params functorParams;
+ /// Initialize the parameters for 2D output tensor
+ CUTLASS_HOST_DEVICE int initialize(Index m_,
+ Index n_,
+ ScalarAlphaBeta alpha_,
+ ScalarAlphaBeta beta_,
+ long long int reduction_stride_,
+ ScalarA const *d_a_,
+ Index lda_,
+ ScalarC const *d_c_,
+ Index ldc_,
+ ScalarD *d_d_,
+ Index ldd_){
+ problem_size = make_Coord(1, n_, m_);
+ alpha = alpha_;
+ beta = beta_;
+ reduction_stride = reduction_stride_;
+ d_a = d_a_;
+ lda = lda_;
+ d_c = d_c_;
+ d_d = d_d_;
+ ldc = ldc_;
+ ldd = ldd_;
+
+ functorParams.initialize(alpha_, beta_);
+
+ return 0;
+ }
+ };
+
+};
+} // namespace reduction
+} // namespace cutlass
diff --git a/cutlass/reduction/threadblock_swizzle.h b/cutlass/reduction/threadblock_swizzle.h
new file mode 100644
index 00000000..8be29eed
--- /dev/null
+++ b/cutlass/reduction/threadblock_swizzle.h
@@ -0,0 +1,61 @@
+/***************************************************************************************************
+* 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 Defies functors for mapping blockIdx to partitions of the batched reduction computation.
+*/
+#pragma once
+#include "cutlass/coord.h"
+
+namespace cutlass {
+namespace reduction {
+struct DefaultBlockSwizzle {
+ /// Ctor
+ CUTLASS_HOST_DEVICE DefaultBlockSwizzle() {}
+
+ /// Swizzle the block index.
+ CUTLASS_DEVICE dim3 swizzle() { return blockIdx; }
+
+ ///
+ CUTLASS_HOST_DEVICE dim3 get_grid_layout(Coord<3> const &problem_size,
+ Coord<3> const &OutputTile) {
+ assert(OutputTile[0] == 1 && OutputTile[1] == 1);
+ assert((problem_size[0] * problem_size[1] * problem_size[2]) % OutputTile[2] == 0);
+ dim3 grid;
+ grid.x = problem_size[0] * problem_size[1] * problem_size[2]
+ / OutputTile[2] ;
+ return grid;
+ }
+
+ ///
+ CUTLASS_DEVICE Coord<3> get_threadblock_offset(Coord<3> const &SubTile) {
+ assert(SubTile[0] == 1 && SubTile[1] == 1);
+ dim3 block = swizzle();
+ Coord<3> threadblock_offset =
+ make_Coord(0, 0, block.x * SubTile[2]);
+ return threadblock_offset;
+ }
+};
+} // namespace reduction
+} // namespace cutlass
diff --git a/cutlass/reshape_tile.h b/cutlass/reshape_tile.h
index 67faa602..2ae51220 100644
--- a/cutlass/reshape_tile.h
+++ b/cutlass/reshape_tile.h
@@ -53,6 +53,22 @@ struct ReshapeTile {
typedef Shape Tile;
};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+template
+struct WmmaReshapeTile {
+ typedef Tile_ Tile;
+};
+
+template
+struct WmmaReshapeTile {
+ // Make sure the W dimension of the tile is large enough.
+ static_assert(Tile_::kW >= (kAccessSize_ * kLdsPerAccess_), "The W dimension is too small");
+ // Make sure the dimension can be divided by the number of scalars.
+ static_assert(Tile_::kW % (kAccessSize_ * kLdsPerAccess_) == 0, "Not supported");
+ // Collapse the W dimension.
+ typedef Shape Tile;
+};
+
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace cutlass
diff --git a/cutlass/tensor_ref_collection.h b/cutlass/tensor_ref_collection.h
index b2972e18..79c0d268 100644
--- a/cutlass/tensor_ref_collection.h
+++ b/cutlass/tensor_ref_collection.h
@@ -23,7 +23,7 @@
*
**************************************************************************************************/
/*! \file
- \brief Introduces TensorRefCollection concept and defines TensorRefBatch and TensorRefArray.
+ \brief Introduces TensorRefCollection concept and defines TensorRefBatch and TensorRefArray.
*/
#pragma once
@@ -85,7 +85,7 @@ template <
/// Index type used for offsets and pointer differences
typename LongIndex_ = long long
>
-struct TensorRefBatchStrided:
+struct TensorRefBatchStrided:
public TensorRef {
//
@@ -98,12 +98,16 @@ struct TensorRefBatchStrided:
/// Storage type
typedef typename Base::Storage Storage;
+ /// Rank of the logical tensor
+ static int const kRank = Rank_;
+
/// Index type
typedef Index_ Index;
/// Typically, strides in memory can be very large
typedef LongIndex_ LongIndex;
+
/// Coordinate in logical tensor space
typedef Coord TensorCoord;
@@ -121,7 +125,7 @@ struct TensorRefBatchStrided:
/// Reference to the parent TensorBatchRef object
TensorRefBatchStrided const &ref_;
- /// Offset from the base TensorRef pointer
+ /// Offset from the base TensorRef pointer
LongIndex offset_;
public:
@@ -129,12 +133,12 @@ struct TensorRefBatchStrided:
/// Constructs a ConstIterator from a parent TensorRefBatchStrided
CUTLASS_HOST_DEVICE
ConstIterator(
- TensorRefBatchStrided const &ref,
+ TensorRefBatchStrided const &ref,
LongIndex offset = 0): ref_(ref), offset_(offset) { }
/// Obtains a TensorRef pointed to by the iterator
CUTLASS_HOST_DEVICE
- TensorRef *operator() const {
+ TensorRef operator*() const {
TensorRef ref(ref_);
ref.add_pointer_offset(offset_);
return ref;
@@ -158,7 +162,7 @@ struct TensorRefBatchStrided:
/// Returns an iterator advanced by (idx) amount
CUTLASS_HOST_DEVICE
ConstIterator operator+(Index idx) {
- return ConstIterator(ref, offset_ + ref_.tensor_stride * idx);
+ return ConstIterator(ref_, offset_ + ref_.tensor_stride * idx);
}
/// Advances this iterator by (idx) and returns a reference to self
@@ -198,7 +202,7 @@ struct TensorRefBatchStrided:
/// Returns the difference in offset between two iterators
CUTLASS_HOST_DEVICE
- Stride operator-(ConstIterator const &it) {
+ LongIndex operator-(ConstIterator const &it) {
return offset_ - it.offset_;
}
};
@@ -218,10 +222,10 @@ struct TensorRefBatchStrided:
CUTLASS_HOST_DEVICE
TensorRefBatchStrided(): tensor_stride(0) { }
- // Constructs form a tensor reference and
+ // Constructs form a tensor reference and
CUTLASS_HOST_DEVICE
- TensorRefBatchStrided(TensorRef const &ref, LongIndex _tensor_stride = 0):
- TensorRef(ref),
+ TensorRefBatchStrided(TensorRef const &ref, LongIndex _tensor_stride = 0):
+ TensorRef(ref),
tensor_stride(_tensor_stride) { }
/// Gets the pointer offset
@@ -232,7 +236,7 @@ struct TensorRefBatchStrided:
// Returns a reference
CUTLASS_HOST_DEVICE
- TensorRef at(Index idx) const {
+ TensorRef at(Index idx = 0) const {
TensorRef ref(*this);
ref.add_pointer_offset(get_pointer_offset(idx));
return ref;
@@ -245,6 +249,30 @@ struct TensorRefBatchStrided:
}
};
+/// Helper to construct a TensorRefBatchStrided<> object using type deduction
+template
+CUTLASS_HOST_DEVICE
+TensorRefBatchStrided<
+ typename TensorRef_::Storage,
+ TensorRef_::kRank,
+ typename TensorRef_::MapFunc,
+ TensorRef_::kStorageGrank,
+ typename TensorRef_::Index,
+ typename TensorRef_::LongIndex
+> make_TensorRefBatchStrided(
+ TensorRef_ const &ref,
+ typename TensorRef_::LongIndex batch_stride = 0) {
+
+ return TensorRefBatchStrided<
+ typename TensorRef_::Storage,
+ TensorRef_::kRank,
+ typename TensorRef_::MapFunc,
+ TensorRef_::kStorageGrank,
+ typename TensorRef_::Index,
+ typename TensorRef_::LongIndex
+ >(ref, batch_stride);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/// This satisfies TensorRefCollection and stores a collection of TensorRef objects. This is a
@@ -253,7 +281,7 @@ struct TensorRefBatchStrided:
/// Note, TensorRef maps a logical coordinate space to an n-D array with rank kStorageRank. It
/// maintains a stride vector of similar rank, but the least significant rank is defined to be 1.
///
-/// The least significant stride of 1 is not stored, and therefore the number of stride arrays is
+/// The least significant stride of 1 is not stored, and therefore the number of stride arrays is
/// kStorageRank - 1.
template <
/// Data type of element stored within tensor
@@ -274,9 +302,6 @@ struct TensorRefArray {
// Type definitions
//
- /// TensorRef type obtained from the TensorRefArray
- typedef TensorRef TensorRef;
-
/// Element pointed to by the TensorRef
typedef Storage_ Storage;
@@ -287,16 +312,17 @@ struct TensorRefArray {
typedef LongIndex_ LongIndex;
/// Rank of the stride vector
- static int const kStorageRank = TensorRef::kStorageRank;
+ static int const kStorageRank = StorageRank_;
- /// TensorRefIterator over TensorRef objects in TensorRefArray
+ /// TensorRefIterator over TensorRef objects in TensorRefArray
class ConstIterator {
public:
- /// TensorRef returned by the iterator
- typedef Base TensorRef;
+ /// Containing class's tensor rev
+ typedef TensorRef TensorRef;
private:
+
/// Reference to the TensorRefArray
TensorRefArray const &ref_;
@@ -307,11 +333,11 @@ struct TensorRefArray {
/// Constructs a ConstIterator over the TensorRef objects
CUTLASS_HOST_DEVICE
- ConstIterator(TensorArrayRef const &ref, int idx = 0): ref_(ref), idx_(idx) { }
+ ConstIterator(TensorRefArray const &ref, int idx = 0): ref_(ref), idx_(idx) { }
/// Obtains a TensorRef pointed to by this iterator
CUTLASS_HOST_DEVICE
- TensorRef *operator() const {
+ TensorRef operator*() const {
return ref_.reference(idx_);
}
@@ -367,6 +393,9 @@ struct TensorRefArray {
}
};
+ /// TensorRef type obtained from the TensorRefArray
+ typedef TensorRef TensorRef;
+
//
// Data members
//
@@ -383,13 +412,13 @@ struct TensorRefArray {
// Default ctor
CUTLASS_HOST_DEVICE
- TensorArrayRef() { }
+ TensorRefArray() { }
// Construct from pointers to arrays to strides
CUTLASS_HOST_DEVICE
- TensorArrayRef(
+ TensorRefArray(
Storage **_pointers,
- Index _strides[kStorageRank - 1]): pointers(_pointers) {
+ Index _strides[kStorageRank - 1]): pointers(_pointers) {
// Copy pointers to strides arrays
for (int i = 0; i < kStorageRank - 1; ++i) {
@@ -399,11 +428,11 @@ struct TensorRefArray {
// Returns a TensorRef at the given index in the collection
CUTLASS_HOST_DEVICE
- TensorRef at(Index idx) const {
+ TensorRef at(Index idx = 0) const {
Coord stride;
CUTLASS_PRAGMA_UNROLL
for (int i = 0; i < kStorageRank - 1; ++i) {
- stride[i] = stride_[idx][i];
+ stride[i] = strides[idx][i];
}
return TensorRef(pointers[idx], stride);
}
diff --git a/cutlass/tile_allocation.h b/cutlass/tile_allocation.h
index 81db797f..873f67d0 100644
--- a/cutlass/tile_allocation.h
+++ b/cutlass/tile_allocation.h
@@ -30,6 +30,7 @@
#include "cutlass/shape.h"
#include "cutlass/fragment.h"
#include "cutlass/tensor_ref.h"
+#include "cutlass/tensor_view.h"
#include "cutlass/zip_tensor_ref.h"
namespace cutlass {
@@ -61,6 +62,12 @@ struct TileAllocation {
/// Defines the tensor reference for this allocation
typedef TensorRef TensorRef;
+ /// View of memory
+ typedef TensorView ConstTensorView;
+
+ /// View of memory
+ typedef TensorView TensorView;
+
//
// Data members
//
@@ -91,6 +98,24 @@ struct TileAllocation {
ConstTensorRef reference() const {
return ConstTensorRef(data(), make_Coord(Strides::kD, Strides::kH, Strides::kW, Strides::kC));
}
+
+ /// Returns a TensorView object pointing to the data
+ CUTLASS_DEVICE
+ TensorView view() {
+ return TensorView(
+ data(),
+ make_Coord(Strides::kD, Strides::kH, Strides::kW, Strides::kC),
+ make_Coord(Shape::kD, Shape::kH, Shape::kW, Shape::kC));
+ }
+
+ /// Returns a TensorView object pointing to the data
+ CUTLASS_DEVICE
+ ConstTensorView view() const {
+ return TensorView(
+ data(),
+ make_Coord(Strides::kD, Strides::kH, Strides::kW, Strides::kC),
+ make_Coord(Shape::kD, Shape::kH, Shape::kW, Shape::kC));
+ }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/cutlass/tile_iterator.h b/cutlass/tile_iterator.h
index 51e57794..71b2e554 100644
--- a/cutlass/tile_iterator.h
+++ b/cutlass/tile_iterator.h
@@ -163,6 +163,9 @@ struct TileIteratorBase {
/// Index type
typedef Index_ Index;
+ /// Long index
+ typedef long long LongIndex;
+
/// Skew quantity
typedef Skew_ Skew;
@@ -216,15 +219,15 @@ struct TileIteratorBase {
// Dat members
//
- long long stride_d;
+ Index stride_d;
Index stride_h;
Index stride_w;
- long long inc_d;
+ Index inc_d;
Index inc_h;
Index inc_w;
- long long inc_advance;
+ Index inc_advance;
//
// Methods
@@ -236,13 +239,13 @@ struct TileIteratorBase {
/// Constructs params
CUTLASS_HOST_DEVICE
- Params(long long _stride_d,
+ Params(Index _stride_d,
Index _stride_h,
Index _stride_w,
- long long _inc_d,
+ Index _inc_d,
Index _inc_h,
Index _inc_w,
- long long _inc_advance)
+ Index _inc_advance)
: stride_d(_stride_d),
stride_h(_stride_h),
stride_w(_stride_w),
@@ -259,13 +262,13 @@ struct TileIteratorBase {
/// Initializes params
CUTLASS_HOST_DEVICE
- int initialize(long long _stride_d,
+ int initialize(Index _stride_d,
Index _stride_h,
Index _stride_w,
- long long _inc_d,
+ Index _inc_d,
Index _inc_h,
Index _inc_w,
- long long _inc_advance) {
+ Index _inc_advance) {
stride_d = _stride_d;
stride_h = _stride_h;
stride_w = _stride_w;
@@ -286,14 +289,14 @@ struct TileIteratorBase {
/// Initializes the parameters object from a vector of strides
CUTLASS_HOST_DEVICE
- int initialize(long long _stride_d, Index _stride_h, Index _stride_w) {
+ int initialize(Index _stride_d, Index _stride_h, Index _stride_w) {
stride_d = _stride_d;
stride_h = _stride_h;
stride_w = _stride_w;
inc_w = stride_w * Delta::kW;
inc_h = stride_h * Delta::kH - stride_w * Delta::kW * (Iterations::kW - 1);
- inc_d = stride_d * Delta::kD - stride_h * Delta::kH * (Iterations::kH - 1) -
+ inc_d = stride_h * Delta::kD - stride_h * Delta::kH * (Iterations::kH - 1) -
stride_w * Delta::kW * (Iterations::kW - 1);
inc_advance = 0;
@@ -310,7 +313,7 @@ struct TileIteratorBase {
inc_advance = Tile::kD * stride_d;
}
- inc_advance -= stride_d * Delta::kD * (Iterations::kD - 1) +
+ inc_advance -= stride_h * Delta::kD * (Iterations::kD - 1) +
stride_h * Delta::kH * (Iterations::kH - 1) +
stride_w * Delta::kW * (Iterations::kW - 1);
@@ -436,6 +439,9 @@ struct TileLoadIterator : public TileIteratorBase
CUTLASS_HOST_DEVICE void load_post_increment(Fragment &fragment, PredicateIterator pred_it) {
FragmentIterator frag_iterator(fragment);
-
for (int d = 0; d < Iterations::kD; ++d) {
for (int h = 0; h < Iterations::kH; ++h) {
for (int w = 0; w < Iterations::kW; ++w, ++pred_it) {
@@ -876,6 +881,9 @@ struct TileStoreIterator : public TileIteratorBase
+*/
+
+#pragma once
+
+namespace cutlass {
+namespace platform {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Constructs an iterator from a pair of iterators
+template
+struct Pair {
+
+ typedef T1 first_type;
+ typedef T2 second_type;
+
+ //
+ // Data members
+ //
+
+ T1 first;
+ T1 second;
+
+ //
+ // Methods
+ //
+
+ /// Default constructor
+ CUTLASS_HOST_DEVICE
+ Pair() { }
+
+ /// Constructs a pair
+ CUTLASS_HOST_DEVICE
+ Pair(T1 const &first_, T2 const &second_): first(first_), second(second_) { }
+};
+
+/// Constructs a pair and deduces types
+template
+Pair make_Pair(T1 const &first, T2 const &second) {
+ return Pair(first, second);
+}
+
+/// Equality
+template
+CUTLASS_HOST_DEVICE
+bool operator==(Pair const &lhs, Pair const &rhs) {
+ return (lhs.first == rhs.first) && (lhs.second == rhs.second);
+}
+
+/// Inequality
+template
+CUTLASS_HOST_DEVICE
+bool operator!=(Pair const &lhs, Pair const &rhs) {
+ return !(lhs == rhs);
+}
+
+/// Lexical comparison
+template
+CUTLASS_HOST_DEVICE
+bool operator<(Pair const &lhs, Pair const &rhs) {
+ if (lhs.first < rhs.first) {
+ return true;
+ }
+ else if (rhs.first < lhs.first) {
+ return false;
+ }
+ else if (rhs.second < rhs.second) {
+ return false;
+ }
+ return false;
+}
+
+/// Lexical comparison
+template
+CUTLASS_HOST_DEVICE
+bool operator<=(Pair const &lhs, Pair const &rhs) {
+ return !(rhs < lhs);
+}
+
+/// Lexical comparison
+template
+CUTLASS_HOST_DEVICE
+bool operator>(Pair const &lhs, Pair const &rhs) {
+ return (rhs < lhs);
+}
+
+/// Lexical comparison
+template
+CUTLASS_HOST_DEVICE
+bool operator>=(Pair const &lhs, Pair const &rhs) {
+ return !(lhs < rhs);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+} // namespace platform
+} // namespace cutlass
diff --git a/cutlass/util/performance_tuning.h b/cutlass/util/performance_tuning.h
new file mode 100644
index 00000000..fd117740
--- /dev/null
+++ b/cutlass/util/performance_tuning.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * 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
+#ifndef CUTLASS_PERFORMANCE_TUNING_H
+#define CUTLASS_PERFORMANCE_TUNING_H
+
+// CUTLASS_PRAGMA_(UNROLL|NO_UNROLL) optimization directives for the CUDA compiler.
+
+#if defined(__CUDA_ARCH__)
+#if defined(_MSC_VER)
+#define CUTLASS_PRAGMA_UNROLL __pragma("unroll")
+#define CUTLASS_PRAGMA_NO_UNROLL __pragma("unroll 1")
+#else
+#define CUTLASS_PRAGMA_UNROLL _Pragma("unroll")
+#define CUTLASS_PRAGMA_NO_UNROLL _Pragma("unroll 1")
+#endif
+#else
+#define CUTLASS_PRAGMA_UNROLL
+#define CUTLASS_PRAGMA_NO_UNROLL
+#endif
+
+#define CUTLASS_GEMM_LOOP CUTLASS_PRAGMA_NO_UNROLL
+#endif // CUTLASS_PERFORMANCE_TUNING_H
diff --git a/cutlass/zip_tile_iterator.h b/cutlass/zip_tile_iterator.h
index f8ba4eee..f95acc1a 100644
--- a/cutlass/zip_tile_iterator.h
+++ b/cutlass/zip_tile_iterator.h
@@ -32,6 +32,7 @@
#include "cutlass/coord.h"
#include "cutlass/zip_tensor_ref.h"
#include "cutlass/zip_fragment.h"
+#include "cutlass/util/pair.h"
namespace cutlass {
@@ -72,7 +73,10 @@ class ZipTileIterator {
typedef typename First::PredicateVector PredicateVector;
/// Index type
- typedef typename First::Index Index;
+ typedef platform::Pair Index;
+
+ /// Long index type
+ typedef platform::Pair LongIndex;
/// Tensor reference
typedef ZipTensorRef<
@@ -276,9 +280,9 @@ class ZipTileIterator {
CUTLASS_DEVICE ZipTileIterator &operator-=(int count) { return decrement(count); }
/// Adds an offset to both iterators
- CUTLASS_DEVICE void add_pointer_offset(Index offset) {
- first.add_pointer_offset(offset);
- second.add_pointer_offset(offset);
+ CUTLASS_DEVICE void add_pointer_offset(LongIndex offset) {
+ first.add_pointer_offset(offset.first);
+ second.add_pointer_offset(offset.second);
}
};
diff --git a/examples/02_cutlass_utilities/cutlass_utilities.cu b/examples/02_cutlass_utilities/cutlass_utilities.cu
index 6b3d6454..7ca79c80 100644
--- a/examples/02_cutlass_utilities/cutlass_utilities.cu
+++ b/examples/02_cutlass_utilities/cutlass_utilities.cu
@@ -103,6 +103,7 @@
// Defines cutlass::reference::host::Gemm()
#include "tools/util/reference/host/gemm.h"
+#pragma warning( disable : 4503)
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Define a CUTLASS GEMM template and launch a GEMM kernel.
@@ -144,18 +145,18 @@ cudaError_t Cutlass_FP16_SgemmNN(
typename Gemm::Params params;
int result = params.initialize(
- M, // GEMM M dimension
- N, // GEMM N dimension
- K, // GEMM K dimension
- reinterpret_cast(alpha), // scalar alpha - This is a legal conversion from cutlass::half_t to CUDA's half.
- A, // matrix A operand
+ M, // GEMM M dimension
+ N, // GEMM N dimension
+ K, // GEMM K dimension
+ reinterpret_cast(alpha), // scalar alpha
+ A, // matrix A operand
lda,
- B, // matrix B operand
+ B, // matrix B operand
ldb,
- reinterpret_cast(beta), // scalar beta - This is a legal conversion from cutlass::half_t to CUDA's half.
- C, // source matrix C
+ reinterpret_cast(beta), // scalar beta
+ C, // source matrix C
ldc,
- C, // destination matrix C (may be different memory than source C matrix)
+ C, // destination matrix C (may be different memory than source C matrix)
ldc
);
diff --git a/examples/06_splitK_gemm/CMakeLists.txt b/examples/06_splitK_gemm/CMakeLists.txt
new file mode 100644
index 00000000..695a91b1
--- /dev/null
+++ b/examples/06_splitK_gemm/CMakeLists.txt
@@ -0,0 +1,38 @@
+# 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.
+
+
+set(EXAMPLES_SPLITK_GEMM_SOURCES
+ splitK_gemm.cu
+)
+
+if (NOT CUTLASS_NATIVE_CUDA)
+ # cuda_add_executable does not take interface include directories into account
+ # Let's fetch them and pass them to CUDA.
+ get_target_property(CUTLASS_INCLUDES CUTLASS INTERFACE_INCLUDE_DIRECTORIES)
+ include_directories("${CUTLASS_INCLUDES}")
+endif()
+
+cutlass_add_executable(
+ 06_splitK_gemm
+ ${EXAMPLES_SPLITK_GEMM_SOURCES}
+)
diff --git a/examples/06_splitK_gemm/splitK_gemm.cu b/examples/06_splitK_gemm/splitK_gemm.cu
new file mode 100644
index 00000000..20ea490b
--- /dev/null
+++ b/examples/06_splitK_gemm/splitK_gemm.cu
@@ -0,0 +1,302 @@
+/***************************************************************************************************
+ * 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.
+ *
+ **************************************************************************************************/
+
+#include
+#include
+#include "cutlass/cutlass.h"
+#include "cutlass/gemm/device_gemm.h"
+#include "cutlass/gemm/sgemm_traits.h"
+#include "cutlass/reduction/batched_reduction_traits.h"
+#include "cutlass/gemm/device_gemm_traits.h"
+#pragma warning( disable : 4503)
+/*
+This example demonstrates how to use cutlass to compute sgemm with splitK
+splitK is useful for gemm with small M and N and reasonably large K.
+Because the sizes of M and N are small, the number of threadblocks we can launch is often limited and
+results in under utilization of the hardware.
+splitK allows us to divide a gemm across K dimension by first launching a partitionedK gemm (very similar to batched gemm),
+storing the intermediate result in workspace and then launching a second reduction kernel.
+Thus, as demonstrated by function cutlass_splitK_sgemm_nn(), the users need to create two traits, one for the partitionedK gemm,
+and one for the reduction. The users are also responsible for allocating and releasing the workspace memory. The size of the workspace
+memory can be queried by calling required_workspace_memory_in_byte().
+*/
+
+template
+cudaError_t cutlass_splitK_sgemm_nn(float const *A,
+ int lda,
+ float const *B,
+ int ldb,
+ float *C,
+ int ldc,
+ float alpha,
+ float beta,
+ int m,
+ int n,
+ int k) {
+ cudaError_t result = cudaSuccess;
+
+ // create cutlass gemm traits for the first kernel
+ typedef cutlass::gemm::SgemmTraits > /*the tile for each threadblock*/
+ SgemmTraits;
+
+ // create cutlass batched reduction traits for the second kernel
+ // for reduction D = alpha * Reduction(A) + beta * C
+ typedef cutlass::reduction::BatchedReductionTraits
+ BatchedReductionTraits;
+
+ // create a device gemm that packages gemm traits and batched reduction traits
+ typedef cutlass::gemm::SplitkPIGemmTraits deviceGemmTraits;
+
+ // kernel class
+ typedef typename deviceGemmTraits::KernelClass deviceGemm;
+
+ // Params ctor requires M, N, K sizes
+ typename deviceGemm::Params deviceGemmParams(m, n, k);
+
+ // query if workspace is needed. the workspace size is sizeof(accumulateType) * M * N * splits_count
+ int workspace_size = deviceGemmParams.required_workspace_memory_in_byte();
+ if (workspace_size <= 0) {
+ std::cerr << "splitK workspace_size is smaller than 0" << std::endl;
+ return cudaErrorInvalidValue;
+ }
+
+ // allocate workspace memory
+ float *workspace_ptr;
+ result = cudaMalloc(&workspace_ptr, workspace_size);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMalloc result = " << result << std::endl;
+ return result;
+ }
+
+ // finish init Params
+ deviceGemmParams.initialize(alpha, /*alpha*/
+ A, /*A*/
+ lda, /*lda*/
+ B, /*B*/
+ ldb, /*ldb*/
+ beta, /*beta*/
+ C, /*C*/
+ ldc, /*ldc*/
+ C, /*D, can point to the same memory with C*/
+ ldc, /*ldc*/
+ workspace_ptr /*ptr to workspace*/
+ );
+
+ // launch the kernel
+ deviceGemm::launch(deviceGemmParams);
+ result = cudaDeviceSynchronize();
+ if (result != cudaSuccess) {
+ std::cerr << "launch result = " << result << std::endl;
+ cudaFree(workspace_ptr);
+ return result;
+ }
+
+ // release the workspace memory
+ result = cudaFree(workspace_ptr);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaFree result = " << result << std::endl;
+ }
+
+ return cudaGetLastError();
+}
+
+template
+cudaError_t sgemm_nn_reference(std::vector const &A,
+ int lda,
+ std::vector const &B,
+ int ldb,
+ std::vector &C,
+ int ldc,
+ T alpha,
+ T beta,
+ int m,
+ int n,
+ int k) {
+ /*
+ sgemm
+ */
+
+ cudaError_t result = cudaSuccess;
+ for (int n_idx = 0; n_idx < n; n_idx++) {
+ for (int m_idx = 0; m_idx < m; m_idx++) {
+ T accum = beta * C[n_idx * ldc + m_idx];
+ for (int k_idx = 0; k_idx < k; k_idx++) {
+ accum += alpha
+ * A[k_idx * lda + m_idx]
+ * B[n_idx * ldb + k_idx];
+ }
+ C[n_idx * ldc + m_idx] = accum;
+ }
+ }
+
+ return result;
+}
+
+int main() {
+ int const m = 128;
+ int const n = 128;
+ int const k = 4096;
+ //splits_count should be known at compile time
+ int const splits_count = 80;
+
+ // A, B are non-transpose, column major
+ int const lda = m;
+ int const ldb = k;
+ int const ldc = m;
+
+ int const count_A = lda * k;
+ int const count_B = ldb * n;
+ int const count_C = ldc * n;
+
+ // alpha and beta
+ float alpha = 1.0f;
+ float beta = 2.0f;
+
+ cudaError_t result = cudaSuccess;
+
+ // allocate the host memory
+ std::vector host_A(count_A);
+ std::vector host_B(count_B);
+ std::vector host_C(count_C);
+ std::vector result_C(count_C);
+
+ // allocate the device memory
+ float *A;
+ float *B;
+ float *C;
+
+ result = cudaMalloc(&A, count_A * sizeof(float));
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMalloc result = " << result << std::endl;
+ return result;
+ }
+ result = cudaMalloc(&B, count_B * sizeof(float));
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMalloc result = " << result << std::endl;
+ return result;
+ }
+ result = cudaMalloc(&C, count_C * sizeof(float));
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMalloc result = " << result << std::endl;
+ return result;
+ }
+
+ // fill A
+ for (int col_idx = 0; col_idx < k; col_idx++) {
+ for (int row_idx = 0; row_idx < m; row_idx++) {
+ host_A[row_idx + col_idx * lda] = static_cast((row_idx + col_idx) % 10);
+ }
+ }
+
+ // fill B
+ for (int col_idx = 0; col_idx < n; col_idx++) {
+ for (int row_idx = 0; row_idx < k; row_idx++) {
+ host_B[row_idx + col_idx * ldb] = static_cast((row_idx - col_idx) % 5);
+ }
+ }
+
+ // fill C
+ for (int col_idx = 0; col_idx < n; col_idx++) {
+ for (int row_idx = 0; row_idx < m; row_idx++) {
+ host_C[row_idx + col_idx * ldc] = 1.f;
+ }
+ }
+
+ // ref memory
+ std::vector ref_A(host_A);
+ std::vector ref_B(host_B);
+ std::vector ref_C(host_C);
+ // copy host memory to device
+ result = cudaMemcpy(A, host_A.data(), count_A * sizeof(float), cudaMemcpyHostToDevice);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMemcpy result = " << result << std::endl;
+ return result;
+ }
+ result = cudaMemcpy(B, host_B.data(), count_B * sizeof(float), cudaMemcpyHostToDevice);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMemcpy result = " << result << std::endl;
+ return result;
+ }
+ result = cudaMemcpy(C, host_C.data(), count_C * sizeof(float), cudaMemcpyHostToDevice);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMemcpy result = " << result << std::endl;
+ return result;
+ }
+
+ // run cutlass
+ result = cutlass_splitK_sgemm_nn(A, lda, B, ldb, C, ldc, alpha, beta, m, n, k);
+ if (result != cudaSuccess)
+ return result;
+
+ // copy device memory to host
+ result = cudaMemcpy(result_C.data(), C, count_C * sizeof(float), cudaMemcpyDeviceToHost);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaMemcpy result = " << result << std::endl;
+ return result;
+ }
+
+ //compare with reference code
+ result = sgemm_nn_reference(ref_A, lda, ref_B, ldb, ref_C, ldc, alpha, beta, m, n, k);
+ if (result != 0)
+ return result;
+
+ if (ref_C != result_C) {
+ std::cout << "CUTLASS splitK gemm does not run correctly" << std::endl;
+ return cudaErrorUnknown;
+ }
+
+ // free memory
+ result = cudaFree(A);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaFree result = " << result << std::endl;
+ return result;
+ }
+ result = cudaFree(B);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaFree result = " << result << std::endl;
+ return result;
+ }
+ result = cudaFree(C);
+ if (result != cudaSuccess) {
+ std::cerr << "cudaFree result = " << result << std::endl;
+ return result;
+ }
+
+
+ if (result == cudaSuccess) {
+ std::cout << "Passed." << std::endl;
+ }
+
+ // Exit.
+ return result == cudaSuccess ? 0 : -1;
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 23e75d40..abc1e6ff 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -26,3 +26,4 @@ add_subdirectory(02_cutlass_utilities)
add_subdirectory(03_strided_batched_gemm)
add_subdirectory(04_tile_iterator)
add_subdirectory(05_wmma_gemm)
+add_subdirectory(06_splitK_gemm)
diff --git a/media/images/cutlass-performance-plot.png b/media/images/cutlass-performance-plot.png
index 041d28b3..0af79c5d 100644
Binary files a/media/images/cutlass-performance-plot.png and b/media/images/cutlass-performance-plot.png differ
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 31f3594f..f14d9d42 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -21,6 +21,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include_directories("external/googletest/googletest/include")
+
add_subdirectory(external/googletest/googletest)
add_subdirectory(test)
add_subdirectory(nvrtc)
+
diff --git a/tools/test/perf/CMakeLists.txt b/tools/test/perf/CMakeLists.txt
index 0405adfc..b5b54b5c 100644
--- a/tools/test/perf/CMakeLists.txt
+++ b/tools/test/perf/CMakeLists.txt
@@ -29,6 +29,7 @@ set(CUTLASS_PERF_TEST_HEADERS
performance_result.h
gemm/cublas_dispatch.h
gemm/cutlass_dispatch.h
+ gemm/cutlass_dispatch_splitK_PI.h
gemm/gemm_perf_testbed.h
gemm/gemm_profiler.h
)
@@ -36,9 +37,11 @@ set(CUTLASS_PERF_TEST_HEADERS
set(CUTLASS_PERF_TEST_SOURCES
cutlass_perf_test.cu
gemm/sgemm.cu
+ gemm/sgemm_splitK.cu
gemm/dgemm.cu
gemm/hgemm.cu
gemm/igemm.cu
+ gemm/igemm_splitK.cu
gemm/wmma_gemm.cu
gemm/wmma_binary_gemm.cu
gemm/wmma_integer_gemm.cu
diff --git a/tools/test/perf/gemm/bmma_gemm.cu b/tools/test/perf/gemm/bmma_gemm.cu
deleted file mode 100644
index 147b5a4b..00000000
--- a/tools/test/perf/gemm/bmma_gemm.cu
+++ /dev/null
@@ -1,121 +0,0 @@
-/***************************************************************************************************
- * 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 {nv-internal-release}
-
-#if (defined(__CUDACC__) && (!defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 750))
-#pragma warning( disable : 4503)
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#include "cutlass/gemm/gemm.h"
-#include "cutlass/gemm/bmma_gemm_traits.h"
-#include "tools/test/perf/cutlass_perf_test.h"
-#include "tools/test/perf/gemm/gemm_profiler.h"
-#include "tools/test/perf/gemm/cutlass_dispatch.h"
-#include "tools/test/perf/gemm/gemm_perf_testbed.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-template
-struct BmmaGemmDispatch {
-
- typedef cutlass::gemm::Gemm Gemm;
-
- typedef typename Gemm::Params Params;
-
- /// Indicate warp-level GEMM
- static bool const kThreadMultiplyAdd = false;
-
- static bool const kRunCuBLAS = false;
-
- static cutlass::MatrixLayout::Kind const kLayoutA = Traits::kLayoutA;
- static cutlass::MatrixLayout::Kind const kLayoutB = Traits::kLayoutB;
-
- //
- // Data members
- //
-
- /// Params argument
- Params params;
-
- //
- // Methods
- //
-
- BmmaGemmDispatch() {}
-
- /// Initializes params object
- BmmaGemmDispatch(int m, int n, int k, int alpha,
- cutlass::Vector const* d_a, int lda,
- cutlass::Vector const* d_b, int ldb, int beta,
- int const* d_c, int ldc, int* d_d, int ldd) {
-
- params.initialize(m, n, k * 32, alpha, d_a, lda, d_b, ldb, beta, d_c, ldc, d_d, ldd);
- }
-
- /// Initializes params object
- BmmaGemmDispatch(Params const& _params) : params(_params) {}
-
- /// Launches kernel
- cudaError_t operator()() { return Gemm::launch(params); }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace perf {
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-int profile_bmma_gemm(TestbenchOutput &output, TestbenchOptions const &options, Config const &config) {
- typedef perf::GemmProfiler, cutlass::Vector, int, int, int> GemmProfiler;
-
- int results = 0;
-
- {
-
- typedef cutlass::gemm::BmmaGemmTraits,
- cutlass::Shape<1024, 32, 32>,
- cutlass::MatrixLayout::kRowMajor,
- cutlass::MatrixLayout::kColumnMajor>
- BmmaGemmTraits;
-
- typedef BmmaGemmDispatch Dispatch;
-
- results |= profile_gemm(output, "bmma_gemm_tn", options, config);
- }
-
- return results;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-struct BmmaGemmRegistrar {
- BmmaGemmRegistrar() { RegisterGemmProfileFunc(profile_bmma_gemm); }
-};
-
-volatile BmmaGemmRegistrar _BmmaGemmRegistrar;
-
-} // namespace perf
-
-#endif // if (defined(__CUDACC__) && (!defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 750)
diff --git a/tools/test/perf/gemm/cublas_dispatch.h b/tools/test/perf/gemm/cublas_dispatch.h
index a30e3d96..8bad0452 100644
--- a/tools/test/perf/gemm/cublas_dispatch.h
+++ b/tools/test/perf/gemm/cublas_dispatch.h
@@ -89,4 +89,76 @@ struct CublasGemmDispatch {
}
};
+/// Dispatcher for batched strided cuBLAS kernels
+template
+struct CublasBatchedStridedGemmDispatch {
+ /// Type used for device-side allocations
+ typedef typename cutlass::TypeTraits::device_type ADeviceType;
+ typedef typename cutlass::TypeTraits::device_type BDeviceType;
+ typedef typename cutlass::TypeTraits::device_type CDeviceType;
+ typedef typename cutlass::TypeTraits::device_type AccumulatorDeviceType;
+ typedef typename cutlass::TypeTraits::device_type ScalarDeviceType;
+
+ static cublasOperation_t convert(cutlass::MatrixLayout::Kind layout) {
+ switch (layout) {
+ case cutlass::MatrixLayout::kRowMajor:
+ return CUBLAS_OP_T;
+ case cutlass::MatrixLayout::kColumnMajor:
+ return CUBLAS_OP_N;
+ default:
+ break;
+ }
+ return CUBLAS_OP_N;
+ }
+
+ /// Launches a cuBLAS GEMM kernel
+ cublasStatus_t operator()(cublasHandle_t handle,
+ cutlass::MatrixLayout::Kind layout_a,
+ cutlass::MatrixLayout::Kind layout_b,
+ int m,
+ int n,
+ int k,
+ Scalar alpha,
+ const ADeviceType *A,
+ int lda,
+ long long int batch_stride_A,
+ const BDeviceType *B,
+ int ldb,
+ long long int batch_stride_B,
+ Scalar beta,
+ CDeviceType *C,
+ int ldc,
+ long long int batch_stride_C,
+ int batch_count,
+ cublasGemmAlgo_t algorithm) {
+#if defined(CUDA_VERSION) && CUDA_VERSION >= 9010
+ return cublasGemmStridedBatchedEx(handle,
+ convert(layout_a),
+ convert(layout_b),
+ m,
+ n,
+ k,
+ reinterpret_cast(&alpha),
+ A,
+ cutlass::TypeTraits::cublas_type,
+ lda,
+ batch_stride_A,
+ B,
+ cutlass::TypeTraits::cublas_type,
+ ldb,
+ batch_stride_B,
+ reinterpret_cast(&beta),
+ C,
+ cutlass::TypeTraits::cublas_type,
+ ldc,
+ batch_stride_C,
+ batch_count,
+ cutlass::TypeTraits::cublas_type,
+ algorithm);
+#else
+ return CUBLAS_STATUS_NOT_SUPPORTED;
+#endif
+ }
+};
+
} // namespace perf
diff --git a/tools/test/perf/gemm/cutlass_dispatch.h b/tools/test/perf/gemm/cutlass_dispatch.h
index f6c85ba6..464dab4a 100644
--- a/tools/test/perf/gemm/cutlass_dispatch.h
+++ b/tools/test/perf/gemm/cutlass_dispatch.h
@@ -81,6 +81,32 @@ struct CutlassDispatch {
params.initialize(m, n, k, alpha, d_a, lda, d_b, ldb, beta, d_c, ldc, d_d, ldd);
}
+ /// Initializes batched strided params object
+ CutlassDispatch(Index m,
+ Index n,
+ Index k,
+ ScalarEpilogue alpha,
+ ScalarA const* d_a,
+ Index lda,
+ long long int batch_stride_A,
+ ScalarB const* d_b,
+ Index ldb,
+ long long int batch_stride_B,
+ ScalarEpilogue beta,
+ ScalarC const* d_c,
+ Index ldc,
+ long long int batch_stride_C,
+ ScalarD* d_d,
+ Index ldd,
+ long long int batch_stride_D,
+ Index batch_count) {
+ params.initialize(m, n, k, alpha, d_a, lda, batch_stride_A,
+ d_b, ldb, batch_stride_B,
+ beta, d_c, ldc, batch_stride_C,
+ d_d, ldd, batch_stride_D,
+ batch_count);
+ }
+
/// Initializes params object
CutlassDispatch(Params const& _params) : params(_params) {}
diff --git a/tools/test/perf/gemm/cutlass_dispatch_splitK_PI.h b/tools/test/perf/gemm/cutlass_dispatch_splitK_PI.h
new file mode 100644
index 00000000..262d39eb
--- /dev/null
+++ b/tools/test/perf/gemm/cutlass_dispatch_splitK_PI.h
@@ -0,0 +1,172 @@
+/***************************************************************************************************
+* 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/matrix_traits.h"
+#include "tools/util/type_traits.h"
+#include
+#include
+
+namespace perf {
+
+template
+ struct CutlassDispatchSplitKPIGemm {
+ typedef typename KernelClass_::Params Params;
+ typedef KernelClass_ KernelClass;
+ typedef Index_ Index;
+ typedef ScalarA_ ScalarA;
+ typedef ScalarB_ ScalarB;
+ typedef ScalarC_ ScalarC;
+ typedef ScalarD_ ScalarD;
+ typedef Compute_ Compute;
+ typedef ScalarEpilogue_ ScalarEpilogue;
+
+ static bool const kThreadMultiplyAdd = ThreadMultiplyAdd_;
+ static bool const kRunCuBLAS = RunCuBLAS_;
+
+ static cutlass::MatrixLayout::Kind const kLayoutA = KernelClass::Traits::kLayoutA;
+ static cutlass::MatrixLayout::Kind const kLayoutB = KernelClass::Traits::kLayoutB;
+
+ //
+ // Data members
+ //
+
+ /// Params argument
+ Params params;
+
+ /// splitK PI require workspace
+ typename cutlass::TypeTraits::device_type *workspace_ptr;
+
+ //
+ // Methods
+ //
+
+ /// Ctor Initializes params object
+ CutlassDispatchSplitKPIGemm(Index m,
+ Index n,
+ Index k,
+ ScalarEpilogue alpha,
+ ScalarA const* d_a,
+ Index lda,
+ ScalarB const* d_b,
+ Index ldb,
+ ScalarEpilogue beta,
+ ScalarC const* d_c,
+ Index ldc,
+ ScalarD* d_d,
+ Index ldd) {
+ params.init_problem(m, n, k);
+ int workspace_size_in_byte = params.required_workspace_memory_in_byte();
+
+ cudaError_t workspace_err = cudaMalloc(&workspace_ptr, workspace_size_in_byte);
+ if (workspace_err != cudaSuccess) {
+ std::cout << "\nCUDA workspace malloc error: " << cudaGetErrorString(workspace_err)
+ << "\n";
+ }
+
+ params.initialize(alpha, d_a, lda, d_b, ldb, beta, d_c, ldc, d_d, ldd, workspace_ptr);
+ }
+
+ /// Initializes batched strided params object
+ CutlassDispatchSplitKPIGemm(Index m,
+ Index n,
+ Index k,
+ ScalarEpilogue alpha,
+ ScalarA const* d_a,
+ Index lda,
+ long long int batch_stride_A,
+ ScalarB const* d_b,
+ Index ldb,
+ long long int batch_stride_B,
+ ScalarEpilogue beta,
+ ScalarC const* d_c,
+ Index ldc,
+ long long int batch_stride_C,
+ ScalarD* d_d,
+ Index ldd,
+ long long int batch_stride_D,
+ Index batch_count) {
+ assert(0);//batched strided splitK should never be called
+ }
+
+ /// Launches kernel
+ cudaError_t operator()() { return KernelClass::launch(params); }
+
+ ~CutlassDispatchSplitKPIGemm() {
+ cudaError_t workspace_err = cudaFree(workspace_ptr);
+ if (workspace_err != cudaSuccess) {
+ std::cout << "\nCUDA workspace malloc error: " << cudaGetErrorString(workspace_err)
+ << "\n";
+ }
+ }
+};
+
+template<
+ typename SplitKPIGemmTraits_
+>
+struct CutlassDispatchSplitKPIGemmBasic {
+ ///
+ typedef SplitKPIGemmTraits_ Traits;
+
+ ///
+ typedef typename Traits::KernelClass KernelClass;
+
+ /// Index type
+ typedef typename Traits::Index Index;
+
+ /// The scalar for A.
+ typedef typename Traits::ScalarA ScalarA;
+ /// The scalar for B.
+ typedef typename Traits::ScalarB ScalarB;
+ /// The scalar for C.
+ typedef typename Traits::ScalarC ScalarC;
+ /// The scalar for D.
+ typedef typename Traits::ScalarD ScalarD;
+
+ // TODO - support alternative accumulator and scalar types
+ typedef ScalarD Compute;
+ typedef Compute ScalarEpilogue;
+
+ typedef CutlassDispatchSplitKPIGemm
+ Dispatch;
+};
+} //namespace perf
diff --git a/tools/test/perf/gemm/gemm_perf_testbed.h b/tools/test/perf/gemm/gemm_perf_testbed.h
index 27769b1c..81ba51e1 100644
--- a/tools/test/perf/gemm/gemm_perf_testbed.h
+++ b/tools/test/perf/gemm/gemm_perf_testbed.h
@@ -78,6 +78,7 @@ class GemmTestbed {
/// Dispatch object to cuBLAS GEMM
typedef CublasGemmDispatch CublasDispatch;
+ typedef CublasBatchedStridedGemmDispatch CublasBatchedStridedGemmDispatch;
//
// Type definitions
@@ -160,18 +161,20 @@ class GemmTestbed {
/// Resizes each tensor
void resize_helper(GemmProblem const &problem) {
- resize_device_allocation(A,
- initial_distribution.dist_A,
- initial_distribution.seed,
- problem.m,
- problem.k,
- problem.layout_A);
+
+ resize_device_allocation(A,
+ initial_distribution.dist_A,
+ initial_distribution.seed,
+ problem.m,
+ problem.k * problem.batch_count,
+ problem.layout_A);
+
resize_device_allocation(
B,
initial_distribution.dist_B,
initial_distribution.seed + 17, // compute distinct value from initial seed
- problem.k,
+ problem.k * problem.batch_count,
problem.n,
problem.layout_B);
@@ -180,21 +183,21 @@ class GemmTestbed {
initial_distribution.dist_C,
initial_distribution.seed + 101, // compute distinct value from initial seed
problem.m,
- problem.n,
+ problem.n * problem.batch_count,
cutlass::MatrixLayout::kColumnMajor);
resize_device_allocation(reference,
cutlass::Distribution(),
0,
problem.m,
- problem.n,
+ problem.n * problem.batch_count,
cutlass::MatrixLayout::kColumnMajor);
resize_device_allocation(experimental,
cutlass::Distribution(),
0,
problem.m,
- problem.n,
+ problem.n * problem.batch_count,
cutlass::MatrixLayout::kColumnMajor);
}
@@ -315,24 +318,36 @@ class GemmTestbed {
/// Inner dimension of GEMM problem
int K() const { return problem.k; }
+ /// batch count
+ int batch_count() const { return problem.batch_count; }
+
/// Returns a pointer to the A operand
ADeviceType *ptr_A() const { return A.get(); }
/// Leading dimension of A
int lda() const { return problem.lda(); }
+ ///
+ long long int batch_stride_a() const{ return problem.batch_stride_a(); }
+
/// Returns a pointer to the B operand
BDeviceType *ptr_B() const { return B.get(); }
/// Leading dimension of B
int ldb() const { return problem.ldb(); }
+ ///
+ long long int batch_stride_b() const{ return problem.batch_stride_b(); }
+
/// Returns a pointer to the initial state of the result tensor in device memory
CDeviceType *ptr_C_initial() const { return C_initial.get(); }
/// Leading dimension of C
int ldc() const { return problem.ldc(); }
+ ///
+ long long int batch_stride_c() const { return problem.batch_stride_c(); }
+
/// Returns a pointer to the result tensor in device memory
CDeviceType *ptr_experimental() const { return experimental.get(); }
@@ -341,7 +356,7 @@ class GemmTestbed {
/// Returns the number of flops implied by the computation (1 multiply-accumulate = 2 flops)
uint64_t flops() const {
- return uint64_t(problem.m) * uint64_t(problem.n) * uint64_t(problem.k) * detail::ElementCount::kValue * 2ULL;
+ return uint64_t(problem.batch_count) * uint64_t(problem.m) * uint64_t(problem.n) * uint64_t(problem.k) * detail::ElementCount::kValue * 2ULL;
}
/// Computes the speed of the computation in GFLOPs/s
@@ -373,28 +388,59 @@ class GemmTestbed {
/// Launches the cuBLAS GEMM - does not initialize output matrix
cublasStatus_t launch_cublas(cublasGemmAlgo_t algo) {
- CublasDispatch dispatch;
+ if (problem.batch_count == 1) {
+ CublasDispatch dispatch;
- Scalar alpha(Scalar(problem.alpha));
- Scalar beta(Scalar(problem.beta));
+ Scalar alpha(Scalar(problem.alpha));
+ Scalar beta(Scalar(problem.beta));
- status = dispatch(handle,
- problem.layout_A,
- problem.layout_B,
- problem.m,
- problem.n,
- problem.k,
- alpha,
- ptr_A(),
- lda(),
- ptr_B(),
- ldb(),
- beta,
- ptr_reference(),
- ldc(),
- algo);
+ status = dispatch(handle,
+ problem.layout_A,
+ problem.layout_B,
+ problem.m,
+ problem.n,
+ problem.k,
+ alpha,
+ ptr_A(),
+ lda(),
+ ptr_B(),
+ ldb(),
+ beta,
+ ptr_reference(),
+ ldc(),
+ algo);
- return status;
+ return status;
+ }
+ else {
+ // call batched strided cublas
+ CublasBatchedStridedGemmDispatch dispatch;
+
+ Scalar alpha(Scalar(problem.alpha));
+ Scalar beta(Scalar(problem.beta));
+
+ status = dispatch(handle,
+ problem.layout_A,
+ problem.layout_B,
+ problem.m,
+ problem.n,
+ problem.k,
+ alpha,
+ ptr_A(),
+ lda(),
+ batch_stride_a(),
+ ptr_B(),
+ ldb(),
+ batch_stride_b(),
+ beta,
+ ptr_reference(),
+ ldc(),
+ batch_stride_c(),
+ batch_count(),
+ algo);
+
+ return status;
+ }
}
/// Verifies the 'test' tensor with 'ref'
diff --git a/tools/test/perf/gemm/gemm_profiler.h b/tools/test/perf/gemm/gemm_profiler.h
index 6cdb07b9..82d41514 100644
--- a/tools/test/perf/gemm/gemm_profiler.h
+++ b/tools/test/perf/gemm/gemm_profiler.h
@@ -164,24 +164,52 @@ class GemmProfiler {
result.disposition = Disposition::Passed;
}
- CutlassDispatch dispatch(testbed.M(),
- testbed.N(),
- testbed.K(),
- testbed.alpha(),
- testbed.ptr_A(),
- testbed.lda(),
- testbed.ptr_B(),
- testbed.ldb(),
- testbed.beta(),
- testbed.ptr_C_initial(),
- testbed.ldc(),
- testbed.ptr_experimental(),
- testbed.ldc());
+ CutlassDispatch *dispatch_ptr;
- dispatch();
+ // check to see if we need to launch batched strided gemm
+ if (testbed.batch_count() == 1) {
+ dispatch_ptr = new CutlassDispatch(testbed.M(),
+ testbed.N(),
+ testbed.K(),
+ testbed.alpha(),
+ testbed.ptr_A(),
+ testbed.lda(),
+ testbed.ptr_B(),
+ testbed.ldb(),
+ testbed.beta(),
+ testbed.ptr_C_initial(),
+ testbed.ldc(),
+ testbed.ptr_experimental(),
+ testbed.ldc());
+
+ dispatch_ptr->operator()();
+ }
+ else {
+ dispatch_ptr = new CutlassDispatch(testbed.M(),
+ testbed.N(),
+ testbed.K(),
+ testbed.alpha(),
+ testbed.ptr_A(),
+ testbed.lda(),
+ testbed.batch_stride_a(),
+ testbed.ptr_B(),
+ testbed.ldb(),
+ testbed.batch_stride_b(),
+ testbed.beta(),
+ testbed.ptr_C_initial(),
+ testbed.ldc(),
+ testbed.batch_stride_c(),
+ testbed.ptr_experimental(),
+ testbed.ldc(),
+ testbed.batch_stride_c(),
+ testbed.batch_count());
+
+ dispatch_ptr->operator()();
+ }
if (cudaDeviceSynchronize() != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
@@ -202,35 +230,40 @@ class GemmProfiler {
}
// warmup launch
- dispatch();
+ dispatch_ptr->operator()();
if (cudaDeviceSynchronize() != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
if (cudaEventRecord(events[0]) != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
for (int iter = 0; iter < options.iterations; ++iter) {
- dispatch();
+ dispatch_ptr->operator()();
}
if (cudaEventRecord(events[1]) != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
if (cudaEventSynchronize(events[1]) != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
float average_ms = 0;
if (cudaEventElapsedTime(&average_ms, events[0], events[1]) != cudaSuccess) {
result.disposition = Disposition::Failed;
+ delete dispatch_ptr;
return result;
}
@@ -242,6 +275,7 @@ class GemmProfiler {
<< " failed with disposition: " << result.disposition << "\n";
}
+ delete dispatch_ptr;
return result;
}
@@ -265,7 +299,7 @@ class GemmProfiler {
std::vector > results;
- results.push_back(execute_cutlass(problem, algorithm));
+ results.push_back(execute_cutlass(problem, algorithm));
// cool-down period
if (!options.dry_run) {
pause(options.sleep_time);
@@ -276,28 +310,30 @@ class GemmProfiler {
/// Runs the test and collects performance for all results
template
- void schmoo(Range const &M, Range const &N, Range const &K) {
- for (int m = M.start; m <= M.end; m = M.next(m)) {
- for (int n = N.start; n <= N.end; n = N.next(n)) {
- for (int k = K.start; k <= K.end; k = K.next(k)) {
-
- std::vector > results =
+ void schmoo(Range const &M, Range const &N, Range const &K, Range const &batch_count) {
+ for (int b = batch_count.start; b <= batch_count.end; b = batch_count.next(b)) {
+ for (int m = M.start; m <= M.end; m = M.next(m)) {
+ for (int n = N.start; n <= N.end; n = N.next(n)) {
+ for (int k = K.start; k <= K.end; k = K.next(k)) {
+ std::vector > results =
execute(GemmProblem(m,
- n,
- k,
- CutlassDispatch::kLayoutA,
- CutlassDispatch::kLayoutB,
- config.alpha,
- config.beta));
+ n,
+ k,
+ CutlassDispatch::kLayoutA,
+ CutlassDispatch::kLayoutB,
+ config.alpha,
+ config.beta,
+ b));
- for (std::vector >::const_iterator it = results.begin();
- it != results.end();
- ++it) {
- output.append(*it);
- }
- }
- }
- }
+ for (std::vector >::const_iterator it = results.begin();
+ it != results.end();
+ ++it) {
+ output.append(*it);
+ }
+ }//k
+ }//n
+ }//m
+ }//batch_count
}
/// Runs the test over the problem space and reports only the best performance
@@ -369,7 +405,7 @@ int profile_gemm(TestbenchOutput &output,
config.problem_range.M, config.problem_range.N, config.problem_range.K);
} else {
perf.template schmoo(
- config.problem_range.M, config.problem_range.N, config.problem_range.K);
+ config.problem_range.M, config.problem_range.N, config.problem_range.K, config.problem_range.batch_count);
}
}
diff --git a/tools/test/perf/gemm/igemm_splitK.cu b/tools/test/perf/gemm/igemm_splitK.cu
new file mode 100644
index 00000000..abec1152
--- /dev/null
+++ b/tools/test/perf/gemm/igemm_splitK.cu
@@ -0,0 +1,202 @@
+/***************************************************************************************************
+ * 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.
+ *
+ **************************************************************************************************/
+
+#include "cutlass/gemm/gemm.h"
+#include "cutlass/gemm/igemm_traits.h"
+#include "cutlass/reduction/batched_reduction_traits.h"
+#include "cutlass/gemm/device_gemm_traits.h"
+#include "tools/test/perf/cutlass_perf_test.h"
+#include "tools/test/perf/gemm/gemm_perf_testbed.h"
+#include "tools/test/perf/gemm/gemm_profiler.h"
+#include "tools/test/perf/gemm/cutlass_dispatch.h"
+#include "tools/test/perf/gemm/cutlass_dispatch_splitK_PI.h"
+#pragma warning( disable : 4503)
+
+namespace perf {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template
+int profile_igemm_splitkpi_kernel(
+ TestbenchOutput &output,
+ TestbenchOptions const &options,
+ Config const &config,
+ std::string const &name,
+ std::string const &algo) {
+
+ typedef perf::GemmProfiler GemmProfiler;
+
+ int results = 0;
+
+ {
+ /*batched igemm traits*/
+ typedef cutlass::gemm::IgemmTraits<
+ cutlass::MatrixLayout::kColumnMajor,
+ cutlass::MatrixLayout::kColumnMajor,
+ OutputTile
+ > IgemmTraits;
+ /*batched reduction traits*/
+ typedef cutlass::reduction::BatchedReductionTraits,
+ cutlass::Shape<1, 1, 64>,
+ cutlass::Shape<1, 1, 2> >
+ BatchedReductionTraits;
+
+ // create a device gemm
+ typedef typename cutlass::gemm::SplitkPIGemmTraits deviceGemmTraits;
+ typedef typename CutlassDispatchSplitKPIGemmBasic::Dispatch Dispatch;
+
+ results |= profile_gemm(output, name + "_nn", options, config, algo + "_splitk_pi");
+ }
+
+ {
+ /*batched igemm traits*/
+ typedef cutlass::gemm::IgemmTraits<
+ cutlass::MatrixLayout::kColumnMajor,
+ cutlass::MatrixLayout::kRowMajor,
+ OutputTile
+ > IgemmTraits;
+ /*batched reduction traits*/
+ typedef cutlass::reduction::BatchedReductionTraits,
+ cutlass::Shape<1, 1, 64>,
+ cutlass::Shape<1, 1, 2> >
+ BatchedReductionTraits;
+
+ // create a device gemm
+ typedef typename cutlass::gemm::SplitkPIGemmTraits deviceGemmTraits;
+ typedef typename CutlassDispatchSplitKPIGemmBasic::Dispatch Dispatch;
+
+ results |= profile_gemm(output, name + "_nt", options, config, algo + "_splitk_pi");
+ }
+
+ {
+ /*batched igemm traits*/
+ typedef cutlass::gemm::IgemmTraits<
+ cutlass::MatrixLayout::kRowMajor,
+ cutlass::MatrixLayout::kColumnMajor,
+ OutputTile
+ > IgemmTraits;
+ /*batched reduction traits*/
+ typedef cutlass::reduction::BatchedReductionTraits,
+ cutlass::Shape<1, 1, 64>,
+ cutlass::Shape<1, 1, 2> >
+ BatchedReductionTraits;
+
+ // create a device gemm
+ typedef typename cutlass::gemm::SplitkPIGemmTraits deviceGemmTraits;
+ typedef typename CutlassDispatchSplitKPIGemmBasic::Dispatch Dispatch;
+
+ results |= profile_gemm(output, name + "_tn", options, config, algo + "_splitk_pi");
+ }
+
+ {
+ /*batched igemm traits*/
+ typedef cutlass::gemm::IgemmTraits<
+ cutlass::MatrixLayout::kRowMajor,
+ cutlass::MatrixLayout::kRowMajor,
+ OutputTile
+ > IgemmTraits;
+ /*batched reduction traits*/
+ typedef cutlass::reduction::BatchedReductionTraits,
+ cutlass::Shape<1, 1, 64>,
+ cutlass::Shape<1, 1, 2> >
+ BatchedReductionTraits;
+
+ // create a device gemm
+ typedef typename cutlass::gemm::SplitkPIGemmTraits deviceGemmTraits;
+ typedef typename CutlassDispatchSplitKPIGemmBasic::Dispatch Dispatch;
+
+ results |= profile_gemm(output, name + "_tt", options, config, algo + "_splitk_pi");
+ }
+
+
+ return results;
+}
+
+/// Profiles all SGEMM tile sizes
+int profile_igemm_splitkpi(TestbenchOutput &output, TestbenchOptions const &options, Config const &config) {
+ int results = 0;
+ /*128x128x32*/
+ results |= profile_igemm_splitkpi_kernel, 8 >(output, options, config, "igemm_128x128x32_splitk_pi_split8", "128x128");
+ results |= profile_igemm_splitkpi_kernel, 16 >(output, options, config, "igemm_128x128x32_splitk_pi_split16", "128x128");
+ results |= profile_igemm_splitkpi_kernel, 32 >(output, options, config, "igemm_128x128x32_splitk_pi_split32", "128x128");
+ results |= profile_igemm_splitkpi_kernel, 64 >(output, options, config, "igemm_128x128x32_splitk_pi_split64", "128x128");
+
+ /*128x64x32*/
+ results |= profile_igemm_splitkpi_kernel, 8 >(output, options, config, "igemm_128x64x32_splitk_pi_split8", "128x64");
+ results |= profile_igemm_splitkpi_kernel, 16 >(output, options, config, "igemm_128x64x32_splitk_pi_split16", "128x64");
+ results |= profile_igemm_splitkpi_kernel, 20 >(output, options, config, "igemm_128x64x32_splitk_pi_split20", "128x64");
+ results |= profile_igemm_splitkpi_kernel, 32 >(output, options, config, "igemm_128x64x32_splitk_pi_split32", "128x64");
+ results |= profile_igemm_splitkpi_kernel, 64 >(output, options, config, "igemm_128x64x32_splitk_pi_split64", "128x64");
+
+ /*128x32x32*/
+ results |= profile_igemm_splitkpi_kernel, 8 >(output, options, config, "igemm_128x32x32_splitk_pi_split8", "128x32");
+ results |= profile_igemm_splitkpi_kernel, 16 >(output, options, config, "igemm_128x32x32_splitk_pi_split16", "128x32");
+ results |= profile_igemm_splitkpi_kernel, 20 >(output, options, config, "igemm_128x32x32_splitk_pi_split20", "128x32");
+ results |= profile_igemm_splitkpi_kernel, 32 >(output, options, config, "igemm_128x32x32_splitk_pi_split32", "128x32");
+ results |= profile_igemm_splitkpi_kernel, 64 >(output, options, config, "igemm_128x32x32_splitk_pi_split64", "128x32");
+
+ /*64x64x32*/
+ results |= profile_igemm_splitkpi_kernel, 8 >(output, options, config, "igemm_64x64x32_splitk_pi_split8", "64x64");
+ results |= profile_igemm_splitkpi_kernel, 16 >(output, options, config, "igemm_64x64x32_splitk_pi_split16", "64x64");
+ results |= profile_igemm_splitkpi_kernel, 20 >(output, options, config, "igemm_64x64x32_splitk_pi_split20", "64x64");
+ results |= profile_igemm_splitkpi_kernel, 32 >(output, options, config, "igemm_64x64x32_splitk_pi_split32", "64x64");
+ results |= profile_igemm_splitkpi_kernel, 64 >(output, options, config, "igemm_64x64x32_splitk_pi_split64", "64x64");
+
+ return results;
+}
+
+struct IgemmSplitKPIRegistrar {
+ IgemmSplitKPIRegistrar() { RegisterGemmProfileFunc(profile_igemm_splitkpi); }
+};
+
+volatile IgemmSplitKPIRegistrar _IgemmSplitKPIRegistrar;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+} // namespace perf
diff --git a/tools/test/perf/gemm/sgemm.cu b/tools/test/perf/gemm/sgemm.cu
index 1448ae0d..c83e8748 100644
--- a/tools/test/perf/gemm/sgemm.cu
+++ b/tools/test/perf/gemm/sgemm.cu
@@ -25,10 +25,13 @@
#include "cutlass/gemm/gemm.h"
#include "cutlass/gemm/sgemm_traits.h"
+#include "cutlass/reduction/batched_reduction_traits.h"
+#include "cutlass/gemm/device_gemm_traits.h"
#include "tools/test/perf/cutlass_perf_test.h"
#include "tools/test/perf/gemm/gemm_perf_testbed.h"
#include "tools/test/perf/gemm/gemm_profiler.h"
#include "tools/test/perf/gemm/cutlass_dispatch.h"
+#include "tools/test/perf/gemm/cutlass_dispatch_splitK_PI.h"
#pragma warning( disable : 4503)
namespace perf {
@@ -94,6 +97,1223 @@ int profile_sgemm_kernel(
results |= profile_gemm(output, name + "_tt", options, config, algo);
}
+
+ {
+ typedef int index;
+ typedef cutlass::gemm::SgemmConfig/*ThreadGemmShape*/,
+ 1/*kScalarsPerLdgA*/,
+ 1/*kScalarsPerLdgB*/>
+ thisGemmConfig;
+ typedef cutlass::gemm::GemmTileTraitsHelperA
+ GemmTileTraitsHelperA;
+ typedef cutlass::gemm::GemmTileTraitsHelperB
+ GemmTileTraitsHelperB;
+ typedef cutlass::gemm::SimplifiedGemmTraitsHelper
+ Helper;
+ typedef cutlass::gemm::LinearScaling
+ EpilogueFunctor;
+ typedef cutlass::gemm::SimplifiedGemmEpilogueTraits
+ GemmEpilogueTraits;
+ typedef cutlass::gemm::ClearAccumulators
+ ClearAccumulators;
+
+ typedef cutlass::gemm::GemmTraits<
+ thisGemmConfig,
+ typename Helper::GlobalLoadStreamA,
+ typename Helper::GlobalLoadStreamB,
+ typename Helper::SharedLoadStreamA,
+ typename Helper::SharedLoadStreamB,
+ typename cutlass::gemm::GemmEpilogue,
+ typename cutlass::gemm::RowMajorBlockSwizzle<1, cutlass::gemm::swizzleDirection::OneDirection>,
+ index,
+ ClearAccumulators
+ >
+ SgemmTraits;
+
+ typedef typename CutlassDispatchBasic::Dispatch Dispatch;
+
+ results |= profile_gemm