CUTLASS 2.0 (#62)
CUTLASS 2.0 Substantially refactored for - Better performance, particularly for native Turing Tensor Cores - Robust and durable templates spanning the design space - Encapsulated functionality embodying modern C++11 programming techniques - Optimized containers and data types for efficient, generic, portable device code Updates to: - Quick start guide - Documentation - Utilities - CUTLASS Profiler Native Turing Tensor Cores - Efficient GEMM kernels targeting Turing Tensor Cores - Mixed-precision floating point, 8-bit integer, 4-bit integer, and binarized operands Coverage of existing CUTLASS functionality: - GEMM kernels targeting CUDA and Tensor Cores in NVIDIA GPUs - Volta Tensor Cores through native mma.sync and through WMMA API - Optimizations such as parallel reductions, threadblock rasterization, and intra-threadblock reductions - Batched GEMM operations - Complex-valued GEMMs Note: this commit and all that follow require a host compiler supporting C++11 or greater.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Helper functions for mapping CUTLASS concepts to cuBLAS.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#if CUTLASS_ENABLE_CUBLAS
|
||||
#include "cublas_helpers.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Converts a cuBLAS status to cutlass::Status
|
||||
Status get_cutlass_status(cublasStatus_t cublas) {
|
||||
|
||||
if (cublas == CUBLAS_STATUS_SUCCESS) {
|
||||
return Status::kSuccess;
|
||||
}
|
||||
else if (cublas == CUBLAS_STATUS_INVALID_VALUE) {
|
||||
return Status::kErrorInvalidProblem;
|
||||
}
|
||||
if (cublas == CUBLAS_STATUS_NOT_SUPPORTED) {
|
||||
return Status::kErrorNotSupported;
|
||||
}
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
|
||||
/// Maps a CUTLASS tensor layout to a cuBLAS transpose operation
|
||||
cublasOperation_t get_cublas_transpose_operation(library::LayoutTypeID layout) {
|
||||
switch (layout) {
|
||||
case library::LayoutTypeID::kColumnMajor:
|
||||
return CUBLAS_OP_N;
|
||||
case library::LayoutTypeID::kRowMajor:
|
||||
return CUBLAS_OP_T;
|
||||
default: break;
|
||||
}
|
||||
throw std::runtime_error("CUTLASS layout type does not correspond to cublas type");
|
||||
}
|
||||
|
||||
/// Maps a CUTLASS numeric type to a cuBLAS data type enumeration
|
||||
bool get_cublas_datatype(cublasDataType_t &data_type, library::NumericTypeID element_type) {
|
||||
switch (element_type) {
|
||||
case library::NumericTypeID::kF16:
|
||||
data_type = CUDA_R_16F;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kF32:
|
||||
data_type = CUDA_R_32F;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kF64:
|
||||
data_type = CUDA_R_64F;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kS4:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS8:
|
||||
data_type = CUDA_R_8I;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kS16:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS32:
|
||||
data_type = CUDA_R_32I;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kS64:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU4:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU8:
|
||||
data_type = CUDA_R_8U;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kU16:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU32:
|
||||
data_type = CUDA_R_32U;
|
||||
return true;
|
||||
|
||||
case library::NumericTypeID::kU64:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kB1:
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kInvalid:
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Gets the cublas algorithm given threadblock tile dimensions and math opcode class
|
||||
cublasGemmAlgo_t get_cublas_gemm_algo(int cta_m, int cta_n, int cta_k, library::OpcodeClassID opcode_class) {
|
||||
// TODO
|
||||
return (opcode_class == library::OpcodeClassID::kSimt ?
|
||||
CUBLAS_GEMM_DEFAULT : CUBLAS_GEMM_DEFAULT_TENSOR_OP);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns a status if cuBLAS can satisfy a particular GEMM description
|
||||
Status cublas_satisfies(library::GemmDescription const &desc) {
|
||||
auto const &math_instruction = desc.tile_description.math_instruction;
|
||||
|
||||
if (math_instruction.element_accumulator == library::NumericTypeID::kS32 &&
|
||||
math_instruction.opcode_class == library::OpcodeClassID::kTensorOp) {
|
||||
|
||||
return Status::kErrorNotSupported;
|
||||
}
|
||||
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
#endif // #if CUTLASS_ENABLE_CUBLAS
|
||||
@@ -0,0 +1,93 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Helper functions for mapping CUTLASS concepts to cuBLAS.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CUTLASS_ENABLE_CUBLAS
|
||||
#include <cublas_v2.h>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/library/library.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Converts a cuBLAS status to cutlass::Status
|
||||
Status get_cutlass_status(cublasStatus_t cublas);
|
||||
|
||||
/// Maps a CUTLASS tensor layout to a cuBLAS transpose operation
|
||||
cublasOperation_t get_cublas_transpose_operation(library::LayoutTypeID layout);
|
||||
|
||||
/// Maps a CUTLASS numeric type to a cuBLAS data type enumeration
|
||||
bool get_cublas_datatype(cublasDataType_t &data_type, library::NumericTypeID element_type);
|
||||
|
||||
/// Gets the cublas algorithm given threadblock tile dimensions and math opcode class
|
||||
cublasGemmAlgo_t get_cublas_gemm_algo(
|
||||
int cta_m,
|
||||
int cta_n,
|
||||
int cta_k,
|
||||
library::OpcodeClassID opcode_class);
|
||||
|
||||
/// Returns a status if cuBLAS can satisfy a particular GEMM description
|
||||
Status cublas_satisfies(library::GemmDescription const &desc);
|
||||
|
||||
/// This is a helper class to create cublasHandle_t automatically on CublasCreate object creation and
|
||||
/// to destroy cublasHandle_t on CublasCreate object destruction.
|
||||
/// Additionaly, it provides implicit cast from CublasCreate's object to cublasHandle_t's object
|
||||
class CublasCreate {
|
||||
private:
|
||||
cublasHandle_t handle;
|
||||
cublasStatus_t status;
|
||||
|
||||
public:
|
||||
CublasCreate() {
|
||||
status = cublasCreate(&handle);
|
||||
}
|
||||
|
||||
~CublasCreate() {
|
||||
cublasDestroy(handle);
|
||||
}
|
||||
|
||||
/// Implicit cast CublasCreate object to cublasHandle_t
|
||||
operator cublasHandle_t() const { return handle; }
|
||||
|
||||
/// returns cublasStatus_t for handle creation
|
||||
cublasStatus_t get_cublas_create_status() { return status; }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
|
||||
#endif // #if CUTLASS_ENABLE_CUBLAS
|
||||
@@ -0,0 +1,194 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
// CUTLASS Library includes
|
||||
#include "cutlass/library/library.h"
|
||||
#include "cutlass/library/manifest.h"
|
||||
|
||||
// Profiler includes
|
||||
#include "cutlass_profiler.h"
|
||||
#include "gemm_operation_profiler.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CutlassProfiler::CutlassProfiler(
|
||||
Options const &options
|
||||
):
|
||||
options_(options) {
|
||||
|
||||
operation_profilers_.emplace_back(new GemmOperationProfiler);
|
||||
}
|
||||
|
||||
CutlassProfiler::~CutlassProfiler() {
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Execute the program
|
||||
int CutlassProfiler::operator()() {
|
||||
|
||||
if (options_.about.help) {
|
||||
if (options_.operation_kind == library::OperationKind::kInvalid) {
|
||||
print_usage_(std::cout);
|
||||
}
|
||||
else {
|
||||
for (auto & profiler : operation_profilers_) {
|
||||
if (profiler->kind() == options_.operation_kind) {
|
||||
profiler->print_usage(std::cout);
|
||||
profiler->print_examples(std::cout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (options_.about.version) {
|
||||
options_.about.print_version(std::cout);
|
||||
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
else if (options_.about.device_info) {
|
||||
options_.device.print_device_info(std::cout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (options_.execution_mode == ExecutionMode::kProfile ||
|
||||
options_.execution_mode == ExecutionMode::kDryRun ||
|
||||
options_.execution_mode == ExecutionMode::kTrace) {
|
||||
|
||||
// Profiles all operations
|
||||
profile_();
|
||||
}
|
||||
else if (options_.execution_mode == ExecutionMode::kEnumerate) {
|
||||
// Enumerates all operations
|
||||
enumerate_();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Enumerates all operations
|
||||
void CutlassProfiler::enumerate_() {
|
||||
|
||||
}
|
||||
|
||||
/// Profiles all operations
|
||||
int CutlassProfiler::profile_() {
|
||||
|
||||
library::Manifest manifest;
|
||||
Status status = manifest.initialize();
|
||||
|
||||
if (status != Status::kSuccess) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
DeviceContext device_context;
|
||||
|
||||
// For all profilers
|
||||
for (auto & profiler : operation_profilers_) {
|
||||
|
||||
if (options_.operation_kind == library::OperationKind::kInvalid ||
|
||||
options_.operation_kind == profiler->kind()) {
|
||||
|
||||
result = profiler->profile_all(options_, manifest, device_context);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Prints all options
|
||||
void CutlassProfiler::print_usage_(std::ostream &out) {
|
||||
options_.print_usage(out);
|
||||
|
||||
out << "\nOperations:\n\n";
|
||||
|
||||
// For all profilers
|
||||
for (auto & profiler : operation_profilers_) {
|
||||
|
||||
|
||||
std::string kind_str = library::to_string(profiler->kind());
|
||||
|
||||
size_t kAlignment = 40;
|
||||
size_t columns = 0;
|
||||
|
||||
if (kind_str.size() < kAlignment) {
|
||||
columns = kAlignment - kind_str.size();
|
||||
}
|
||||
|
||||
out << " " << kind_str << std::string(columns, ' ') << profiler->description() << "\n";
|
||||
|
||||
}
|
||||
|
||||
out << "\n\nFor details about a particular function, specify the function name with --help.\n\nExample:\n\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --help\n\n";
|
||||
}
|
||||
|
||||
/// Prints usage
|
||||
void CutlassProfiler::print_options_(std::ostream &out) {
|
||||
options_.print_options(out);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Initializes the CUDA device
|
||||
void CutlassProfiler::initialize_device_() {
|
||||
|
||||
cudaError_t result = cudaSetDevice(options_.device.device);
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
std::cerr << "Failed to set device.";
|
||||
throw std::runtime_error("Failed to set device");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,86 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "options.h"
|
||||
#include "operation_profiler.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// CUTLASS Profiler application
|
||||
class CutlassProfiler {
|
||||
private:
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Performance testbench options
|
||||
Options options_;
|
||||
|
||||
/// Entry points for each operation
|
||||
OperationProfilerVector operation_profilers_;
|
||||
|
||||
private:
|
||||
|
||||
/// Prints usage
|
||||
void print_usage_(std::ostream &);
|
||||
|
||||
/// Prints usage
|
||||
void print_options_(std::ostream &);
|
||||
|
||||
/// Initializes the device
|
||||
void initialize_device_();
|
||||
|
||||
/// Enumerates all operations
|
||||
void enumerate_();
|
||||
|
||||
/// Profiles all operations
|
||||
int profile_();
|
||||
|
||||
public:
|
||||
|
||||
CutlassProfiler(Options const &options);
|
||||
~CutlassProfiler();
|
||||
|
||||
/// Invokes profiling operations
|
||||
int operator()();
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,50 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define report(x) { std::cout << "\033[31m" << __FILE__ << ":" << __LINE__ << " " << x << "\033[0m" << std::endl; }
|
||||
//#define report(x) {}
|
||||
|
||||
// Enable/Disble Profiler debug prints
|
||||
#define DEBUG_PROFILER
|
||||
|
||||
//RED 31m // profiler prints debug messages in red
|
||||
//YELLOW 33m // ir prints debug messages in yellow
|
||||
|
||||
#ifndef DEBUG_PROFILER
|
||||
#define debugprof(...)
|
||||
#else
|
||||
#define debugprof(...) do { \
|
||||
printf("\033[31m[DEBUG PROF] %s:%d | ", __FILE__, __LINE__); \
|
||||
printf(__VA_ARGS__); \
|
||||
printf("\033[0m\n"); \
|
||||
} while (0)
|
||||
#endif
|
||||
@@ -0,0 +1,922 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "cutlass/numeric_types.h"
|
||||
#include "cutlass/layout/matrix.h"
|
||||
#include "cutlass/layout/tensor.h"
|
||||
|
||||
#include "cutlass/util/reference/device/tensor_compare.h"
|
||||
#include "cutlass/util/reference/device/tensor_fill.h"
|
||||
|
||||
#include "cutlass/util/reference/host/tensor_fill.h"
|
||||
|
||||
#include "cutlass/util/host_tensor.h"
|
||||
#include "cutlass/util/tensor_view_io.h"
|
||||
|
||||
#include "device_allocation.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t DeviceAllocation::bytes(library::NumericTypeID type, size_t capacity) {
|
||||
return size_t(cutlass::library::sizeof_bits(type)) * capacity / 8;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Layout>
|
||||
static std::vector<int> get_packed_layout_stride(std::vector<int> const &extent) {
|
||||
|
||||
typename Layout::TensorCoord extent_coord;
|
||||
typename Layout::Stride stride_coord;
|
||||
|
||||
if (extent.size() != size_t(Layout::kRank)) {
|
||||
throw std::runtime_error("Layout does not have same rank as extent vector.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < Layout::kRank; ++i) {
|
||||
extent_coord[i] = extent.at(i);
|
||||
}
|
||||
|
||||
std::vector<int> stride;
|
||||
stride.resize(Layout::kStrideRank, 0);
|
||||
|
||||
Layout layout = Layout::packed(extent_coord);
|
||||
stride_coord = layout.stride();
|
||||
|
||||
for (int i = 0; i < Layout::kStrideRank; ++i) {
|
||||
stride.at(i) = stride_coord[i];
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
/// Returns the stride of a packed layout
|
||||
std::vector<int> DeviceAllocation::get_packed_layout(
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent) {
|
||||
|
||||
std::vector<int> stride;
|
||||
|
||||
switch (layout_id) {
|
||||
case library::LayoutTypeID::kColumnMajor:
|
||||
stride = get_packed_layout_stride<cutlass::layout::ColumnMajor>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kRowMajor:
|
||||
stride = get_packed_layout_stride<cutlass::layout::RowMajor>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kColumnMajorInterleavedK4:
|
||||
stride = get_packed_layout_stride<cutlass::layout::ColumnMajorInterleaved<4>>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kRowMajorInterleavedK4:
|
||||
stride = get_packed_layout_stride<cutlass::layout::RowMajorInterleaved<4>>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kColumnMajorInterleavedK16:
|
||||
stride = get_packed_layout_stride<cutlass::layout::ColumnMajorInterleaved<16>>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kRowMajorInterleavedK16:
|
||||
stride = get_packed_layout_stride<cutlass::layout::RowMajorInterleaved<16>>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kTensorNCHW:
|
||||
stride = get_packed_layout_stride<cutlass::layout::TensorNCHW>(extent);
|
||||
break;
|
||||
case library::LayoutTypeID::kTensorNHWC:
|
||||
stride = get_packed_layout_stride<cutlass::layout::TensorNHWC>(extent);
|
||||
break;
|
||||
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Template to use CUTLASS Layout functions to
|
||||
template <typename Layout>
|
||||
static size_t construct_layout_(
|
||||
void *bytes,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> &stride) {
|
||||
|
||||
if (extent.size() != Layout::kRank) {
|
||||
throw std::runtime_error(
|
||||
"Layout must have same rank as extent vector.");
|
||||
}
|
||||
|
||||
if (Layout::kStrideRank && stride.empty()) {
|
||||
|
||||
stride = get_packed_layout_stride<Layout>(extent);
|
||||
|
||||
return construct_layout_<Layout>(
|
||||
bytes,
|
||||
layout_id,
|
||||
extent,
|
||||
stride);
|
||||
}
|
||||
else if (Layout::kStrideRank && stride.size() != Layout::kStrideRank) {
|
||||
throw std::runtime_error(
|
||||
"Layout requires either empty stride or stride vector matching Layout::kStrideRank");
|
||||
}
|
||||
|
||||
typename Layout::Stride stride_coord;
|
||||
for (int i = 0; i < Layout::kStrideRank; ++i) {
|
||||
stride_coord[i] = stride.at(i);
|
||||
}
|
||||
|
||||
typename Layout::TensorCoord extent_coord;
|
||||
for (int i = 0; i < Layout::kRank; ++i) {
|
||||
extent_coord[i] = extent.at(i);
|
||||
}
|
||||
|
||||
// Construct the CUTLASS layout object from the stride object
|
||||
Layout layout(stride_coord);
|
||||
|
||||
// Pack it into bytes
|
||||
if (bytes) {
|
||||
*reinterpret_cast<Layout *>(bytes) = layout;
|
||||
}
|
||||
|
||||
// Return capacity
|
||||
size_t capacity_ = layout.capacity(extent_coord);
|
||||
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
/// returns the capacity needed
|
||||
size_t DeviceAllocation::construct_layout(
|
||||
void *bytes,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> &stride) {
|
||||
|
||||
switch (layout_id) {
|
||||
case library::LayoutTypeID::kColumnMajor:
|
||||
return construct_layout_<cutlass::layout::ColumnMajor>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kRowMajor:
|
||||
return construct_layout_<cutlass::layout::RowMajor>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kColumnMajorInterleavedK4:
|
||||
return construct_layout_<cutlass::layout::ColumnMajorInterleaved<4>>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kRowMajorInterleavedK4:
|
||||
return construct_layout_<cutlass::layout::RowMajorInterleaved<4>>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kColumnMajorInterleavedK16:
|
||||
return construct_layout_<cutlass::layout::ColumnMajorInterleaved<16>>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kRowMajorInterleavedK16:
|
||||
return construct_layout_<cutlass::layout::RowMajorInterleaved<16>>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kTensorNCHW:
|
||||
return construct_layout_<cutlass::layout::TensorNHWC>(bytes, layout_id, extent, stride);
|
||||
|
||||
case library::LayoutTypeID::kTensorNHWC:
|
||||
return construct_layout_<cutlass::layout::TensorNHWC>(bytes, layout_id, extent, stride);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DeviceAllocation::DeviceAllocation():
|
||||
type_(library::NumericTypeID::kInvalid),
|
||||
capacity_(0),
|
||||
pointer_(nullptr),
|
||||
layout_(library::LayoutTypeID::kUnknown) {
|
||||
|
||||
}
|
||||
|
||||
DeviceAllocation::DeviceAllocation(
|
||||
library::NumericTypeID type,
|
||||
size_t capacity
|
||||
):
|
||||
type_(type), capacity_(capacity), pointer_(nullptr), layout_(library::LayoutTypeID::kUnknown) {
|
||||
|
||||
cudaError_t result = cudaMalloc((void **)&pointer_, bytes(type, capacity));
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
type_ = library::NumericTypeID::kInvalid;
|
||||
capacity_ = 0;
|
||||
pointer_ = nullptr;
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
DeviceAllocation::DeviceAllocation(
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride
|
||||
):
|
||||
type_(type), capacity_(size_t(0)), pointer_(nullptr) {
|
||||
|
||||
reset(type, layout_id, extent, stride);
|
||||
}
|
||||
|
||||
DeviceAllocation::~DeviceAllocation() {
|
||||
if (pointer_) {
|
||||
cudaFree(pointer_);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceAllocation &DeviceAllocation::reset() {
|
||||
if (pointer_) {
|
||||
cudaFree(pointer_);
|
||||
}
|
||||
|
||||
type_ = library::NumericTypeID::kInvalid;
|
||||
capacity_ = 0;
|
||||
pointer_ = nullptr;
|
||||
layout_ = library::LayoutTypeID::kUnknown;
|
||||
stride_.clear();
|
||||
extent_.clear();
|
||||
tensor_ref_buffer_.clear();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DeviceAllocation &DeviceAllocation::reset(library::NumericTypeID type, size_t capacity) {
|
||||
|
||||
reset();
|
||||
|
||||
cudaError_t result = cudaMalloc((void **)&pointer_, bytes(type, capacity));
|
||||
if (result != cudaSuccess) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
type_ = type;
|
||||
capacity_ = capacity;
|
||||
layout_ = library::LayoutTypeID::kUnknown;
|
||||
stride_.clear();
|
||||
extent_.clear();
|
||||
|
||||
tensor_ref_buffer_.resize(sizeof(pointer_), 0);
|
||||
std::memcpy(tensor_ref_buffer_.data(), &pointer_, sizeof(pointer_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allocates memory for a given layout and tensor
|
||||
DeviceAllocation &DeviceAllocation::reset(
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride) {
|
||||
|
||||
reset();
|
||||
|
||||
tensor_ref_buffer_.resize(sizeof(pointer_) + (sizeof(int) * library::get_layout_stride_rank(layout_id)), 0);
|
||||
|
||||
type_ = type;
|
||||
|
||||
layout_ = layout_id;
|
||||
stride_ = stride;
|
||||
extent_ = extent;
|
||||
|
||||
capacity_ = construct_layout(
|
||||
tensor_ref_buffer_.data() + sizeof(pointer_),
|
||||
layout_id,
|
||||
extent,
|
||||
stride_);
|
||||
|
||||
cudaError_t result = cudaMalloc((void **)&pointer_, bytes(type, capacity_));
|
||||
if (result != cudaSuccess) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
std::memcpy(tensor_ref_buffer_.data(), &pointer_, sizeof(pointer_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DeviceAllocation::good() const {
|
||||
return (capacity_ && pointer_);
|
||||
}
|
||||
|
||||
library::NumericTypeID DeviceAllocation::type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
void *DeviceAllocation::data() const {
|
||||
return pointer_;
|
||||
}
|
||||
|
||||
library::LayoutTypeID DeviceAllocation::layout() const {
|
||||
return layout_;
|
||||
}
|
||||
|
||||
std::vector<int> const & DeviceAllocation::stride() const {
|
||||
return stride_;
|
||||
}
|
||||
|
||||
/// Gets the extent vector
|
||||
std::vector<int> const & DeviceAllocation::extent() const {
|
||||
return extent_;
|
||||
}
|
||||
|
||||
size_t DeviceAllocation::capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
size_t DeviceAllocation::bytes() const {
|
||||
return bytes(type_, capacity_);
|
||||
}
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void DeviceAllocation::copy_from_device(void const *ptr) {
|
||||
cudaError_t result = cudaMemcpy(data(), ptr, bytes(), cudaMemcpyDeviceToDevice);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed device-to-device copy");
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void DeviceAllocation::copy_from_host(void const *ptr) {
|
||||
cudaError_t result = cudaMemcpy(data(), ptr, bytes(), cudaMemcpyHostToDevice);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed device-to-device copy");
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void DeviceAllocation::copy_to_host(void *ptr) {
|
||||
cudaError_t result = cudaMemcpy(ptr, data(), bytes(), cudaMemcpyDeviceToHost);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed device-to-device copy");
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocation::initialize_random_device(int seed, Distribution dist) {
|
||||
if (!good()) {
|
||||
throw std::runtime_error("Attempting to initialize invalid allocation.");
|
||||
}
|
||||
|
||||
// Instantiate calls to CURAND here. This file takes a long time to compile for
|
||||
// this reason.
|
||||
|
||||
switch (type_) {
|
||||
case library::NumericTypeID::kF16:
|
||||
cutlass::reference::device::BlockFillRandom<cutlass::half_t>(
|
||||
reinterpret_cast<cutlass::half_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kF32:
|
||||
cutlass::reference::device::BlockFillRandom<float>(
|
||||
reinterpret_cast<float *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kF64:
|
||||
cutlass::reference::device::BlockFillRandom<double>(
|
||||
reinterpret_cast<double *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS8:
|
||||
cutlass::reference::device::BlockFillRandom<int8_t>(
|
||||
reinterpret_cast<int8_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS16:
|
||||
cutlass::reference::device::BlockFillRandom<int16_t>(
|
||||
reinterpret_cast<int16_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS32:
|
||||
cutlass::reference::device::BlockFillRandom<int32_t>(
|
||||
reinterpret_cast<int32_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS64:
|
||||
cutlass::reference::device::BlockFillRandom<int64_t>(
|
||||
reinterpret_cast<int64_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU8:
|
||||
cutlass::reference::device::BlockFillRandom<uint8_t>(
|
||||
reinterpret_cast<uint8_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU16:
|
||||
cutlass::reference::device::BlockFillRandom<uint16_t>(
|
||||
reinterpret_cast<uint16_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU32:
|
||||
cutlass::reference::device::BlockFillRandom<uint32_t>(
|
||||
reinterpret_cast<uint32_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU64:
|
||||
cutlass::reference::device::BlockFillRandom<uint64_t>(
|
||||
reinterpret_cast<uint64_t *>(pointer_),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DeviceAllocation::initialize_random_host(int seed, Distribution dist) {
|
||||
if (!good()) {
|
||||
throw std::runtime_error("Attempting to initialize invalid allocation.");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> host_data(bytes());
|
||||
|
||||
switch (type_) {
|
||||
case library::NumericTypeID::kF16:
|
||||
cutlass::reference::host::BlockFillRandom<cutlass::half_t>(
|
||||
reinterpret_cast<cutlass::half_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kF32:
|
||||
cutlass::reference::host::BlockFillRandom<float>(
|
||||
reinterpret_cast<float *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kF64:
|
||||
cutlass::reference::host::BlockFillRandom<double>(
|
||||
reinterpret_cast<double *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS8:
|
||||
cutlass::reference::host::BlockFillRandom<int8_t>(
|
||||
reinterpret_cast<int8_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS16:
|
||||
cutlass::reference::host::BlockFillRandom<int16_t>(
|
||||
reinterpret_cast<int16_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS32:
|
||||
cutlass::reference::host::BlockFillRandom<int32_t>(
|
||||
reinterpret_cast<int32_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kS64:
|
||||
cutlass::reference::host::BlockFillRandom<int64_t>(
|
||||
reinterpret_cast<int64_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU8:
|
||||
cutlass::reference::host::BlockFillRandom<uint8_t>(
|
||||
reinterpret_cast<uint8_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU16:
|
||||
cutlass::reference::host::BlockFillRandom<uint16_t>(
|
||||
reinterpret_cast<uint16_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU32:
|
||||
cutlass::reference::host::BlockFillRandom<uint32_t>(
|
||||
reinterpret_cast<uint32_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
case library::NumericTypeID::kU64:
|
||||
cutlass::reference::host::BlockFillRandom<uint64_t>(
|
||||
reinterpret_cast<uint64_t *>(host_data.data()),
|
||||
capacity_,
|
||||
seed,
|
||||
dist
|
||||
);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
copy_from_host(host_data.data());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if two blocks have exactly the same value
|
||||
bool DeviceAllocation::block_compare_equal(
|
||||
library::NumericTypeID numeric_type,
|
||||
void const *ptr_A,
|
||||
void const *ptr_B,
|
||||
size_t capacity) {
|
||||
|
||||
switch (numeric_type) {
|
||||
case library::NumericTypeID::kF16:
|
||||
return reference::device::BlockCompareEqual<half_t>(
|
||||
reinterpret_cast<half_t const *>(ptr_A),
|
||||
reinterpret_cast<half_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kF32:
|
||||
return reference::device::BlockCompareEqual<float>(
|
||||
reinterpret_cast<float const *>(ptr_A),
|
||||
reinterpret_cast<float const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kF64:
|
||||
return reference::device::BlockCompareEqual<double>(
|
||||
reinterpret_cast<double const *>(ptr_A),
|
||||
reinterpret_cast<double const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kS8:
|
||||
return reference::device::BlockCompareEqual<int8_t>(
|
||||
reinterpret_cast<int8_t const *>(ptr_A),
|
||||
reinterpret_cast<int8_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kS16:
|
||||
return reference::device::BlockCompareEqual<int16_t>(
|
||||
reinterpret_cast<int16_t const *>(ptr_A),
|
||||
reinterpret_cast<int16_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kS32:
|
||||
return reference::device::BlockCompareEqual<int32_t>(
|
||||
reinterpret_cast<int32_t const *>(ptr_A),
|
||||
reinterpret_cast<int32_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kS64:
|
||||
return reference::device::BlockCompareEqual<int64_t>(
|
||||
reinterpret_cast<int64_t const *>(ptr_A),
|
||||
reinterpret_cast<int64_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kU8:
|
||||
return reference::device::BlockCompareEqual<uint8_t>(
|
||||
reinterpret_cast<uint8_t const *>(ptr_A),
|
||||
reinterpret_cast<uint8_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kU16:
|
||||
return reference::device::BlockCompareEqual<uint16_t>(
|
||||
reinterpret_cast<uint16_t const *>(ptr_A),
|
||||
reinterpret_cast<uint16_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kU32:
|
||||
return reference::device::BlockCompareEqual<uint32_t>(
|
||||
reinterpret_cast<uint32_t const *>(ptr_A),
|
||||
reinterpret_cast<uint32_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
case library::NumericTypeID::kU64:
|
||||
return reference::device::BlockCompareEqual<uint64_t>(
|
||||
reinterpret_cast<uint64_t const *>(ptr_A),
|
||||
reinterpret_cast<uint64_t const *>(ptr_B),
|
||||
capacity);
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported numeric type");
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if two blocks have approximately the same value
|
||||
bool DeviceAllocation::block_compare_relatively_equal(
|
||||
library::NumericTypeID numeric_type,
|
||||
void const *ptr_A,
|
||||
void const *ptr_B,
|
||||
size_t capacity,
|
||||
double epsilon,
|
||||
double nonzero_floor) {
|
||||
|
||||
switch (numeric_type) {
|
||||
case library::NumericTypeID::kF16:
|
||||
return reference::device::BlockCompareRelativelyEqual<half_t>(
|
||||
reinterpret_cast<half_t const *>(ptr_A),
|
||||
reinterpret_cast<half_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<half_t>(epsilon),
|
||||
static_cast<half_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kF32:
|
||||
return reference::device::BlockCompareRelativelyEqual<float>(
|
||||
reinterpret_cast<float const *>(ptr_A),
|
||||
reinterpret_cast<float const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<float>(epsilon),
|
||||
static_cast<float>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kF64:
|
||||
return reference::device::BlockCompareRelativelyEqual<double>(
|
||||
reinterpret_cast<double const *>(ptr_A),
|
||||
reinterpret_cast<double const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<double>(epsilon),
|
||||
static_cast<double>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kS8:
|
||||
return reference::device::BlockCompareRelativelyEqual<int8_t>(
|
||||
reinterpret_cast<int8_t const *>(ptr_A),
|
||||
reinterpret_cast<int8_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<int8_t>(epsilon),
|
||||
static_cast<int8_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kS16:
|
||||
return reference::device::BlockCompareRelativelyEqual<int16_t>(
|
||||
reinterpret_cast<int16_t const *>(ptr_A),
|
||||
reinterpret_cast<int16_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<int16_t>(epsilon),
|
||||
static_cast<int16_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kS32:
|
||||
return reference::device::BlockCompareRelativelyEqual<int32_t>(
|
||||
reinterpret_cast<int32_t const *>(ptr_A),
|
||||
reinterpret_cast<int32_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<int32_t>(epsilon),
|
||||
static_cast<int32_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kS64:
|
||||
return reference::device::BlockCompareRelativelyEqual<int64_t>(
|
||||
reinterpret_cast<int64_t const *>(ptr_A),
|
||||
reinterpret_cast<int64_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<int64_t>(epsilon),
|
||||
static_cast<int64_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kU8:
|
||||
return reference::device::BlockCompareRelativelyEqual<uint8_t>(
|
||||
reinterpret_cast<uint8_t const *>(ptr_A),
|
||||
reinterpret_cast<uint8_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<uint8_t>(epsilon),
|
||||
static_cast<uint8_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kU16:
|
||||
return reference::device::BlockCompareRelativelyEqual<uint16_t>(
|
||||
reinterpret_cast<uint16_t const *>(ptr_A),
|
||||
reinterpret_cast<uint16_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<uint16_t>(epsilon),
|
||||
static_cast<uint16_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kU32:
|
||||
return reference::device::BlockCompareRelativelyEqual<uint32_t>(
|
||||
reinterpret_cast<uint32_t const *>(ptr_A),
|
||||
reinterpret_cast<uint32_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<uint32_t>(epsilon),
|
||||
static_cast<uint32_t>(nonzero_floor));
|
||||
|
||||
case library::NumericTypeID::kU64:
|
||||
return reference::device::BlockCompareRelativelyEqual<uint64_t>(
|
||||
reinterpret_cast<uint64_t const *>(ptr_A),
|
||||
reinterpret_cast<uint64_t const *>(ptr_B),
|
||||
capacity,
|
||||
static_cast<uint64_t>(epsilon),
|
||||
static_cast<uint64_t>(nonzero_floor));
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported numeric type");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Permits copying dynamic vectors into static-length vectors
|
||||
template <typename TensorCoord, int Rank>
|
||||
struct vector_to_coord {
|
||||
|
||||
vector_to_coord(TensorCoord &coord, std::vector<int> const &vec) {
|
||||
|
||||
coord[Rank - 1] = vec.at(Rank - 1);
|
||||
|
||||
if (Rank > 1) {
|
||||
vector_to_coord<TensorCoord, Rank - 1>(coord, vec);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Permits copying dynamic vectors into static-length vectors
|
||||
template <typename TensorCoord>
|
||||
struct vector_to_coord<TensorCoord, 1> {
|
||||
|
||||
vector_to_coord(TensorCoord &coord, std::vector<int> const &vec) {
|
||||
|
||||
coord[0] = vec.at(0);
|
||||
}
|
||||
};
|
||||
|
||||
/// Permits copying dynamic vectors into static-length vectors
|
||||
template <typename TensorCoord>
|
||||
struct vector_to_coord<TensorCoord, 0> {
|
||||
|
||||
vector_to_coord(TensorCoord &coord, std::vector<int> const &vec) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Element, typename Layout>
|
||||
static void write_tensor_csv_static_tensor_view(
|
||||
std::ostream &out,
|
||||
DeviceAllocation &allocation) {
|
||||
|
||||
Coord<Layout::kRank> extent;
|
||||
Coord<Layout::kStrideRank> stride;
|
||||
|
||||
if (allocation.extent().size() != Layout::kRank) {
|
||||
throw std::runtime_error("Allocation extent has invalid rank");
|
||||
}
|
||||
|
||||
if (allocation.stride().size() != Layout::kStrideRank) {
|
||||
throw std::runtime_error("Allocation stride has invalid rank");
|
||||
}
|
||||
|
||||
vector_to_coord<Coord<Layout::kRank>, Layout::kRank>(extent, allocation.extent());
|
||||
vector_to_coord<Coord<Layout::kStrideRank>, Layout::kStrideRank>(stride, allocation.stride());
|
||||
|
||||
Layout layout(stride);
|
||||
HostTensor<Element, Layout> host_tensor(extent, layout, false);
|
||||
|
||||
if (host_tensor.capacity() != allocation.capacity()) {
|
||||
throw std::runtime_error("Unexpected capacity to equal.");
|
||||
}
|
||||
|
||||
host_tensor.copy_in_device_to_host(static_cast<Element const *>(allocation.data()), host_tensor.capacity());
|
||||
|
||||
TensorViewWrite(out, host_tensor.host_view());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
static void write_tensor_csv_static_type(
|
||||
std::ostream &out,
|
||||
DeviceAllocation &allocation) {
|
||||
|
||||
switch (allocation.layout()) {
|
||||
case library::LayoutTypeID::kRowMajor:
|
||||
write_tensor_csv_static_tensor_view<T, layout::RowMajor>(out, allocation);
|
||||
break;
|
||||
case library::LayoutTypeID::kColumnMajor:
|
||||
write_tensor_csv_static_tensor_view<T, layout::ColumnMajor>(out, allocation);
|
||||
break;
|
||||
case library::LayoutTypeID::kTensorNHWC:
|
||||
write_tensor_csv_static_tensor_view<T, layout::TensorNHWC>(out, allocation);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unhandled layout");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Writes a tensor to csv
|
||||
void DeviceAllocation::write_tensor_csv(
|
||||
std::ostream &out) {
|
||||
|
||||
switch (this->type()) {
|
||||
case library::NumericTypeID::kF16:
|
||||
write_tensor_csv_static_type<half_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kF32:
|
||||
write_tensor_csv_static_type<float>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kF64:
|
||||
write_tensor_csv_static_type<double>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS8:
|
||||
write_tensor_csv_static_type<int8_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS16:
|
||||
write_tensor_csv_static_type<int16_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS32:
|
||||
write_tensor_csv_static_type<int32_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kS64:
|
||||
write_tensor_csv_static_type<int64_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU8:
|
||||
write_tensor_csv_static_type<uint8_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU16:
|
||||
write_tensor_csv_static_type<uint16_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU32:
|
||||
write_tensor_csv_static_type<uint32_t>(out, *this);
|
||||
break;
|
||||
|
||||
case library::NumericTypeID::kU64:
|
||||
write_tensor_csv_static_type<uint64_t>(out, *this);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported numeric type");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,191 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "cutlass/library/library.h"
|
||||
#include "cutlass/util/distribution.h"
|
||||
|
||||
#include "enumerated_types.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Device memory allocation
|
||||
class DeviceAllocation {
|
||||
private:
|
||||
|
||||
/// Data type of contained elements
|
||||
library::NumericTypeID type_;
|
||||
|
||||
/// Capacity in elements of device allocation
|
||||
size_t capacity_;
|
||||
|
||||
/// Pointer to device memory
|
||||
void *pointer_;
|
||||
|
||||
/// Layout type ID
|
||||
library::LayoutTypeID layout_;
|
||||
|
||||
/// Stride vector
|
||||
std::vector<int> stride_;
|
||||
|
||||
/// Extent vector
|
||||
std::vector<int> extent_;
|
||||
|
||||
/// Buffer holding TensorRef instance to recently allocated memory
|
||||
std::vector<uint8_t> tensor_ref_buffer_;
|
||||
|
||||
public:
|
||||
//
|
||||
// Static member functions
|
||||
//
|
||||
|
||||
/// Determines the number of bytes needed to represent this numeric type
|
||||
static size_t bytes(library::NumericTypeID type, size_t capacity);
|
||||
|
||||
/// Returns the stride of a packed layout
|
||||
static std::vector<int> get_packed_layout(
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent);
|
||||
|
||||
/// returns the capacity needed
|
||||
static size_t construct_layout(
|
||||
void *bytes,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> &stride);
|
||||
|
||||
/// Returns true if two blocks have exactly the same value
|
||||
static bool block_compare_equal(
|
||||
library::NumericTypeID numeric_type,
|
||||
void const *ptr_A,
|
||||
void const *ptr_B,
|
||||
size_t capacity);
|
||||
|
||||
/// Returns true if two blocks have approximately the same value
|
||||
static bool block_compare_relatively_equal(
|
||||
library::NumericTypeID numeric_type,
|
||||
void const *ptr_A,
|
||||
void const *ptr_B,
|
||||
size_t capacity,
|
||||
double epsilon,
|
||||
double nonzero_floor);
|
||||
|
||||
public:
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
DeviceAllocation();
|
||||
|
||||
DeviceAllocation(library::NumericTypeID type, size_t capacity);
|
||||
|
||||
DeviceAllocation(
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride = std::vector<int>());
|
||||
|
||||
~DeviceAllocation();
|
||||
|
||||
DeviceAllocation &reset();
|
||||
|
||||
/// Allocates device memory of a given type and capacity
|
||||
DeviceAllocation &reset(library::NumericTypeID type, size_t capacity);
|
||||
|
||||
/// Allocates memory for a given layout and tensor
|
||||
DeviceAllocation &reset(
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride = std::vector<int>());
|
||||
|
||||
/// Returns a buffer owning the tensor reference
|
||||
std::vector<uint8_t> &tensor_ref() {
|
||||
return tensor_ref_buffer_;
|
||||
}
|
||||
|
||||
bool good() const;
|
||||
|
||||
/// Data type of contained elements
|
||||
library::NumericTypeID type() const;
|
||||
|
||||
/// Pointer to device memory
|
||||
void *data() const;
|
||||
|
||||
/// Gets the layout type
|
||||
library::LayoutTypeID layout() const;
|
||||
|
||||
/// Gets the stride vector
|
||||
std::vector<int> const & stride() const;
|
||||
|
||||
/// Gets the extent vector
|
||||
std::vector<int> const & extent() const;
|
||||
|
||||
/// Capacity of allocation in number of elements
|
||||
size_t capacity() const;
|
||||
|
||||
/// Capacity of allocation in bytes
|
||||
size_t bytes() const;
|
||||
|
||||
/// Initializes a device allocation to a random distribution using cuRAND
|
||||
void initialize_random_device(int seed, Distribution dist);
|
||||
|
||||
/// Initializes a device allocation to a random distribution using cuRAND
|
||||
void initialize_random_host(int seed, Distribution dist);
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void copy_from_device(void const *ptr);
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void copy_from_host(void const *ptr);
|
||||
|
||||
/// Copies from an equivalent-sized tensor in device memory
|
||||
void copy_to_host(void *ptr);
|
||||
|
||||
/// Writes a tensor to csv
|
||||
void write_tensor_csv(std::ostream &out);
|
||||
};
|
||||
|
||||
using DeviceAllocationList = std::list<DeviceAllocation>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,124 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#include "device_context.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *DeviceContext::allocate_block(
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
size_t capacity) {
|
||||
|
||||
device_memory_.emplace_back(type, capacity);
|
||||
DeviceAllocation *allocation = &device_memory_.back();
|
||||
|
||||
allocations_[name] = allocation;
|
||||
return allocation;
|
||||
}
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *DeviceContext::allocate_tensor(
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride) {
|
||||
|
||||
device_memory_.emplace_back(type, layout_id, extent, stride);
|
||||
DeviceAllocation *allocation = &device_memory_.back();
|
||||
|
||||
allocations_[name] = allocation;
|
||||
return allocation;
|
||||
}
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *DeviceContext::allocate_tensor(
|
||||
Options const &options,
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride) {
|
||||
|
||||
DeviceAllocation *allocation =
|
||||
allocate_tensor(name, type, layout_id, extent, stride);
|
||||
|
||||
if (options.initialization.enabled) {
|
||||
|
||||
if (options.initialization.provider == Provider::kReferenceDevice) {
|
||||
allocation->initialize_random_device(
|
||||
options.initialization.seed,
|
||||
options.initialization.data_distribution);
|
||||
}
|
||||
else if (options.initialization.provider == Provider::kReferenceHost) {
|
||||
allocation->initialize_random_host(
|
||||
options.initialization.seed,
|
||||
options.initialization.data_distribution);
|
||||
}
|
||||
}
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
/// Clears named allocations (but does not necessarily free memory)
|
||||
void DeviceContext::clear() {
|
||||
allocations_.clear();
|
||||
}
|
||||
|
||||
/// Frees all device memory allocations
|
||||
void DeviceContext::free() {
|
||||
allocations_.clear();
|
||||
device_memory_.clear();
|
||||
}
|
||||
|
||||
/// Gets the allocation by name
|
||||
DeviceAllocation &DeviceContext::at(std::string const &name) {
|
||||
return *allocations_.at(name);
|
||||
}
|
||||
|
||||
size_t DeviceContext::size() const {
|
||||
return allocations_.size();
|
||||
}
|
||||
|
||||
DeviceContext::AllocationMap::iterator DeviceContext::begin() {
|
||||
return allocations_.begin();
|
||||
}
|
||||
|
||||
DeviceContext::AllocationMap::iterator DeviceContext::end() {
|
||||
return allocations_.end();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,108 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "cutlass/library/library.h"
|
||||
|
||||
#include "options.h"
|
||||
#include "device_allocation.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Collection of allocations on the device
|
||||
class DeviceContext {
|
||||
public:
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
using AllocationMap = std::map<std::string, DeviceAllocation *>;
|
||||
|
||||
private:
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Memory allocations that exist (owning)
|
||||
DeviceAllocationList device_memory_;
|
||||
|
||||
/// Non-owning set of named allocations
|
||||
AllocationMap allocations_;
|
||||
|
||||
public:
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *allocate_block(
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
size_t capacity);
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *allocate_tensor(
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride = std::vector<int>());
|
||||
|
||||
/// Allocates memory of a given type, capacity (elements), and name
|
||||
DeviceAllocation *allocate_tensor(
|
||||
Options const &options,
|
||||
std::string const &name,
|
||||
library::NumericTypeID type,
|
||||
library::LayoutTypeID layout_id,
|
||||
std::vector<int> const &extent,
|
||||
std::vector<int> const &stride = std::vector<int>());
|
||||
|
||||
/// Clears named allocations (but does not necessarily free memory)
|
||||
void clear();
|
||||
|
||||
/// Frees all device memory allocations
|
||||
void free();
|
||||
|
||||
/// Gets the allocation by name
|
||||
DeviceAllocation &at(std::string const &name);
|
||||
|
||||
size_t size() const;
|
||||
|
||||
AllocationMap::iterator begin();
|
||||
AllocationMap::iterator end();
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,315 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Provides several functions for filling tensors with data.
|
||||
*/
|
||||
|
||||
#include "enumerated_types.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
ExecutionMode enumerant;
|
||||
}
|
||||
ExecutionMode_enumerants[] = {
|
||||
{"profile", "Profile", ExecutionMode::kProfile},
|
||||
{"dry_run", "Dry run", ExecutionMode::kDryRun},
|
||||
{"dry", "dry run", ExecutionMode::kDryRun},
|
||||
{"trace", "Trace", ExecutionMode::kTrace},
|
||||
{"enumerate", "Enumerate", ExecutionMode::kEnumerate}
|
||||
};
|
||||
|
||||
/// Converts a ExecutionMode enumerant to a string
|
||||
char const *to_string(ExecutionMode mode, bool pretty) {
|
||||
|
||||
for (auto const & possible : ExecutionMode_enumerants) {
|
||||
if (mode == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a ExecutionMode enumerant from a string
|
||||
template <>
|
||||
ExecutionMode from_string<ExecutionMode>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : ExecutionMode_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return ExecutionMode::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
AlgorithmMode enumerant;
|
||||
}
|
||||
AlgorithmMode_enumerants[] = {
|
||||
{"matching", "Matching", AlgorithmMode::kMatching},
|
||||
{"best", "Best", AlgorithmMode::kBest},
|
||||
{"default", "Default", AlgorithmMode::kDefault}
|
||||
};
|
||||
|
||||
/// Converts a ExecutionMode enumerant to a string
|
||||
char const *to_string(AlgorithmMode mode, bool pretty) {
|
||||
|
||||
for (auto const & possible : AlgorithmMode_enumerants) {
|
||||
if (mode == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a ExecutionMode enumerant from a string
|
||||
template <>
|
||||
AlgorithmMode from_string<AlgorithmMode>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : AlgorithmMode_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return AlgorithmMode::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
Provider enumerant;
|
||||
}
|
||||
Provider_enumerants[] = {
|
||||
{"cutlass", "CUTLASS", Provider::kCUTLASS},
|
||||
{"host", "reference_host", Provider::kReferenceHost},
|
||||
{"device", "reference_device", Provider::kReferenceDevice},
|
||||
{"cublas", "cuBLAS", Provider::kCUBLAS},
|
||||
};
|
||||
|
||||
/// Converts a Provider enumerant to a string
|
||||
char const *to_string(Provider provider, bool pretty) {
|
||||
|
||||
for (auto const & possible : Provider_enumerants) {
|
||||
if (provider == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a Provider enumerant from a string
|
||||
template <>
|
||||
Provider from_string<Provider>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : Provider_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return Provider::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
Disposition enumerant;
|
||||
}
|
||||
Disposition_enumerants[] = {
|
||||
{"passed", "Passed", Disposition::kPassed},
|
||||
{"failed", "Failed", Disposition::kFailed},
|
||||
{"not_run", "Not run", Disposition::kNotRun},
|
||||
{"not_verified", "Not verified", Disposition::kNotVerified},
|
||||
{"not_supported", "Not supported", Disposition::kNotSupported},
|
||||
{"incorrect", "Incorrect", Disposition::kIncorrect}
|
||||
};
|
||||
|
||||
/// Converts a Disposition enumerant to a string
|
||||
char const *to_string(Disposition disposition, bool pretty) {
|
||||
|
||||
for (auto const & possible : Disposition_enumerants) {
|
||||
if (disposition == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a Disposition enumerant from a string
|
||||
template <>
|
||||
Disposition from_string<Disposition>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : Disposition_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return Disposition::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
SaveWorkspace enumerant;
|
||||
}
|
||||
SaveWorkspace_enumerants[] = {
|
||||
{"never", "Never", SaveWorkspace::kNever},
|
||||
{"incorrect", "Incorrect", SaveWorkspace::kIncorrect},
|
||||
{"always", "Always", SaveWorkspace::kAlways}
|
||||
};
|
||||
|
||||
/// Converts a SaveWorkspace enumerant to a string
|
||||
char const *to_string(SaveWorkspace save_option, bool pretty) {
|
||||
|
||||
for (auto const & possible : SaveWorkspace_enumerants) {
|
||||
if (save_option == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a SaveWorkspace enumerant from a string
|
||||
template <>
|
||||
SaveWorkspace from_string<SaveWorkspace>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : SaveWorkspace_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return SaveWorkspace::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static struct {
|
||||
char const *text;
|
||||
char const *pretty;
|
||||
ArgumentTypeID enumerant;
|
||||
}
|
||||
ArgumentTypeID_enumerants[] = {
|
||||
{"scalar", "Scalar", ArgumentTypeID::kScalar},
|
||||
{"int", "Integer", ArgumentTypeID::kInteger},
|
||||
{"tensor", "Tensor", ArgumentTypeID::kTensor},
|
||||
{"batched_tensor", "BatchedTensor", ArgumentTypeID::kBatchedTensor},
|
||||
{"struct", "Struct", ArgumentTypeID::kStructure},
|
||||
{"enum", "Enumerated type", ArgumentTypeID::kEnumerated}
|
||||
};
|
||||
|
||||
/// Converts a ArgumentTypeID enumerant to a string
|
||||
char const *to_string(ArgumentTypeID type, bool pretty) {
|
||||
|
||||
for (auto const & possible : ArgumentTypeID_enumerants) {
|
||||
if (type == possible.enumerant) {
|
||||
if (pretty) {
|
||||
return possible.pretty;
|
||||
}
|
||||
else {
|
||||
return possible.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pretty ? "Invalid" : "invalid";
|
||||
}
|
||||
|
||||
/// Parses a ArgumentTypeID enumerant from a string
|
||||
template <>
|
||||
ArgumentTypeID from_string<ArgumentTypeID>(std::string const &str) {
|
||||
|
||||
for (auto const & possible : ArgumentTypeID_enumerants) {
|
||||
if ((str.compare(possible.text) == 0) ||
|
||||
(str.compare(possible.pretty) == 0)) {
|
||||
return possible.enumerant;
|
||||
}
|
||||
}
|
||||
|
||||
return ArgumentTypeID::kInvalid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Provides several functions for filling tensors with data.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#define TRACE(x) { std::cout << __FILE__ << ":" << __LINE__ << " " << x << std::endl; }
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
T from_string(std::string const &);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Enumerated type describing how the performance testbench evaluates kernels.
|
||||
enum class ExecutionMode {
|
||||
kProfile, ///< regular verification and profiling
|
||||
kDryRun, ///< no kernels are launched or workspaces allocated; used to assess what operators might be launched
|
||||
kEnumerate, ///< no kernels launched or workspaces allocated; lists all function types and functions
|
||||
kTrace, ///< executes a single device-side computation with no other kernel launches
|
||||
kInvalid
|
||||
};
|
||||
|
||||
/// Converts a ExecutionMode enumerant to a string
|
||||
char const *to_string(ExecutionMode mode, bool pretty = false);
|
||||
|
||||
/// Parses a ExecutionMode enumerant from a string
|
||||
template <>
|
||||
ExecutionMode from_string<ExecutionMode>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Library algorithm mode
|
||||
enum class AlgorithmMode {
|
||||
kMatching, ///< compare against best matching algorithm
|
||||
kBest, ///< evaluate all library algorithms and report best
|
||||
kDefault, ///< use the library's default algorithm option
|
||||
kInvalid
|
||||
};
|
||||
|
||||
/// Converts a ExecutionMode enumerant to a string
|
||||
char const *to_string(AlgorithmMode mode, bool pretty = false);
|
||||
|
||||
/// Parses a ExecutionMode enumerant from a string
|
||||
template <>
|
||||
AlgorithmMode from_string<AlgorithmMode>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Providers
|
||||
enum class Provider {
|
||||
kCUTLASS,
|
||||
kReferenceHost,
|
||||
kReferenceDevice,
|
||||
kCUBLAS,
|
||||
kInvalid
|
||||
};
|
||||
|
||||
using ProviderVector = std::vector<Provider>;
|
||||
|
||||
/// Converts a Provider enumerant to a string
|
||||
char const *to_string(Provider provider, bool pretty = false);
|
||||
|
||||
/// Parses a Provider enumerant from a string
|
||||
template <>
|
||||
Provider from_string<Provider>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Outcome of a performance test
|
||||
enum class Disposition {
|
||||
kPassed,
|
||||
kFailed,
|
||||
kNotRun,
|
||||
kIncorrect,
|
||||
kNotVerified,
|
||||
kNotSupported,
|
||||
kInvalid
|
||||
};
|
||||
|
||||
/// Converts a Disposition enumerant to a string
|
||||
char const *to_string(Disposition provider, bool pretty = false);
|
||||
|
||||
/// Parses a Disposition enumerant from a string
|
||||
template <>
|
||||
Disposition from_string<Disposition>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Indicates when to save
|
||||
enum class SaveWorkspace {
|
||||
kNever,
|
||||
kIncorrect,
|
||||
kAlways,
|
||||
kInvalid
|
||||
};
|
||||
|
||||
/// Converts a SaveWorkspace enumerant to a string
|
||||
char const *to_string(SaveWorkspace save_option, bool pretty = false);
|
||||
|
||||
/// Parses a SaveWorkspace enumerant from a string
|
||||
template <>
|
||||
SaveWorkspace from_string<SaveWorkspace>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Indicates the type of kernel argument
|
||||
// ArgumentType can be both ScalarType or NumericType. Thus, enums kScalar and kNumeric
|
||||
// 1) kScalar: e.g. of a Scalar ArgumentType is u32 is a Scalar type.
|
||||
// Its c++ equivalent as "type name = initializer" is "u32 m = 32"
|
||||
// 2) kNumeric: e.g. of a Numeric ArgumentType is NumericTypeID is a Numeric type.
|
||||
// Its c++ equivalent as "type name = initializer" is "NumericTypeID numeric_type = u32"
|
||||
enum class ArgumentTypeID {
|
||||
kScalar,
|
||||
kInteger,
|
||||
kTensor,
|
||||
kBatchedTensor,
|
||||
kStructure,
|
||||
kEnumerated,
|
||||
kInvalid
|
||||
};
|
||||
|
||||
/// Converts a ArgumentTypeID enumerant to a string
|
||||
char const *to_string(ArgumentTypeID type, bool pretty = false);
|
||||
|
||||
/// Parses a ArgumentTypeID enumerant from a string
|
||||
template <>
|
||||
ArgumentTypeID from_string<ArgumentTypeID>(std::string const &str);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,772 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
|
||||
#include "cublas_helpers.h"
|
||||
#include "gemm_operation_profiler.h"
|
||||
#include "gpu_timer.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Ctor
|
||||
GemmOperationProfiler::GemmOperationProfiler():
|
||||
OperationProfiler(library::OperationKind::kGemm,{
|
||||
{ArgumentTypeID::kEnumerated, {"Gemm_kind"}, "Variant of GEMM (e.g. gemm, planar complex, batched, ...)"},
|
||||
{ArgumentTypeID::kInteger, {"m", "problem-size::m"}, "M dimension of the GEMM problem space"},
|
||||
{ArgumentTypeID::kInteger, {"n", "problem-size::n"}, "N dimension of the GEMM problem space"},
|
||||
{ArgumentTypeID::kInteger, {"k", "problem-size::k"}, "K dimension of the GEMM problem space"},
|
||||
{ArgumentTypeID::kTensor, {"A"}, "Tensor storing the A operand"},
|
||||
{ArgumentTypeID::kTensor, {"B"}, "Tensor storing the B operand"},
|
||||
{ArgumentTypeID::kTensor, {"C"}, "Tensor storing the C operand"},
|
||||
{ArgumentTypeID::kScalar, {"alpha", "epilogue::alpha"}, "Epilogue scalar alpha"},
|
||||
{ArgumentTypeID::kScalar, {"beta", "epilogue::beta"}, "Epilogue scalar beta"},
|
||||
{ArgumentTypeID::kInteger, {"split_k_slices"}, "Number of partitions of K dimension"},
|
||||
{ArgumentTypeID::kInteger, {"batch_count"}, "Number of GEMMs computed in one batch"},
|
||||
}) {
|
||||
|
||||
description_ = "General matrix-matrix product. D = alpha * A*B + beta * C";
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
GemmOperationProfiler::~GemmOperationProfiler() {
|
||||
|
||||
}
|
||||
|
||||
/// Prints usage statement for the math function
|
||||
void GemmOperationProfiler::print_usage(std::ostream &out) const {
|
||||
out << "GEMM" << "\n\n";
|
||||
|
||||
OperationProfiler::print_usage(out);
|
||||
}
|
||||
|
||||
/// Prints examples
|
||||
void GemmOperationProfiler::print_examples(std::ostream &out) const {
|
||||
|
||||
out << "\nExamples:\n\n"
|
||||
<< "Profile a particular problem size:\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --m=1024 --n=1024 --k=128\n\n"
|
||||
|
||||
<< "Schmoo over problem size and beta:\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --m=1024:4096:256 --n=1024:4096:256 --k=128:8192:128 --beta=0,1,2.5\n\n"
|
||||
|
||||
<< "Schmoo over accumulator types:\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --accumulator-type=f16,f32\n\n"
|
||||
|
||||
<< "Run when A is f16 with column-major and B is any datatype with row-major (For column major, use column, col, or n. For row major use, row or t):\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --A=f16:column --B=*:row\n\n"
|
||||
|
||||
<< "Using various input value distribution:\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --dist=uniform,min:0,max:3\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --dist=gaussian,mean:0,stddev:3\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --dist=sequential,start:0,delta:1\n\n"
|
||||
|
||||
<< "Run a kernel with cta tile size of 256x128x32 and save workspace if results are incorrect (note that --cta-tile::k=32 is default cta-tile size):\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm --cta_m=256 --cta_n=128 --cta_k=32 --save-workspace=incorrect\n\n"
|
||||
|
||||
<< "Test your changes to gemm kernels with a quick functional test and save results in functional-test.csv:\n"
|
||||
<< " $ cutlass_profiler --operation=Gemm \\ \n"
|
||||
<< " --m=8,56,120,136,256,264,512,520,1024,1032,4096,8192,16384 \\ \n"
|
||||
<< " --n=8,56,120,136,256,264,512,520,1024,1032,4096,8192,16384 \\ \n"
|
||||
<< " --k=8,16,32,64,128,256,288,384,504,512,520 \\ \n"
|
||||
<< " --beta=0,1,2 --profiling-iterations=1 \\ \n"
|
||||
<< " --providers=cutlass --output=functional-test.csv\n\n";
|
||||
}
|
||||
|
||||
#if 0
|
||||
// used this for debugging
|
||||
static std::string byte_string(std::vector<uint8_t> const &bytes) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "0x";
|
||||
|
||||
for (size_t idx = bytes.size(); idx > 0; --idx) {
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << uint32_t(bytes.at(idx - 1));
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Extracts the problem dimensions
|
||||
Status GemmOperationProfiler::initialize_configuration(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
library::GemmDescription const &operation_desc =
|
||||
static_cast<library::GemmDescription const &>(operation->description());
|
||||
|
||||
if (operation_desc.gemm_kind != library::GemmKind::kGemm) {
|
||||
return Status::kErrorInvalidProblem;
|
||||
}
|
||||
|
||||
|
||||
if (!arg_as_int(problem_.m, "m", problem_space, problem)) {
|
||||
// default value
|
||||
problem_.m = 1024;
|
||||
}
|
||||
|
||||
if (!arg_as_int(problem_.n, "n", problem_space, problem)) {
|
||||
// default value
|
||||
problem_.n = 1024;
|
||||
}
|
||||
|
||||
if (!arg_as_int(problem_.k, "k", problem_space, problem)) {
|
||||
// default value
|
||||
problem_.k = 1024;
|
||||
}
|
||||
|
||||
if (!arg_as_int(problem_.split_k_slices, "split_k_slices", problem_space, problem)) {
|
||||
// default value
|
||||
problem_.split_k_slices = 1;
|
||||
}
|
||||
|
||||
if (!arg_as_int(problem_.batch_count, "batch_count", problem_space, problem)) {
|
||||
// default value
|
||||
problem_.batch_count = 1;
|
||||
}
|
||||
|
||||
if (!tensor_description_satisfies(operation_desc.A, "A", problem_space, problem)) {
|
||||
return Status::kErrorInvalidProblem;
|
||||
}
|
||||
|
||||
if (!tensor_description_satisfies(operation_desc.B, "B", problem_space, problem)) {
|
||||
return Status::kErrorInvalidProblem;
|
||||
}
|
||||
|
||||
if (!tensor_description_satisfies(operation_desc.C, "C", problem_space, problem)) {
|
||||
return Status::kErrorInvalidProblem;
|
||||
}
|
||||
|
||||
if (!arg_as_scalar(
|
||||
problem_.alpha,
|
||||
operation_desc.element_epilogue,
|
||||
"alpha",
|
||||
problem_space,
|
||||
problem)) {
|
||||
|
||||
if (!cast_from_double(problem_.alpha, operation_desc.element_epilogue, 1)) {
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_as_scalar(
|
||||
problem_.beta,
|
||||
operation_desc.element_epilogue,
|
||||
"beta",
|
||||
problem_space,
|
||||
problem)) {
|
||||
|
||||
if (!cast_from_double(problem_.beta, operation_desc.element_epilogue, 0)) {
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
|
||||
problem_.lda = DeviceAllocation::get_packed_layout(
|
||||
operation_desc.A.layout, {int(problem_.m), int(problem_.k)}).front();
|
||||
|
||||
problem_.ldb = DeviceAllocation::get_packed_layout(
|
||||
operation_desc.B.layout, {int(problem_.k), int(problem_.n)}).front();
|
||||
|
||||
problem_.ldc = DeviceAllocation::get_packed_layout(
|
||||
operation_desc.C.layout, {int(problem_.m), int(problem_.n)}).front();
|
||||
|
||||
gemm_workspace_.configuration.problem_size.m() = int(problem_.m);
|
||||
gemm_workspace_.configuration.problem_size.n() = int(problem_.n);
|
||||
gemm_workspace_.configuration.problem_size.k() = int(problem_.k);
|
||||
gemm_workspace_.configuration.lda = problem_.lda;
|
||||
gemm_workspace_.configuration.ldb = problem_.ldb;
|
||||
gemm_workspace_.configuration.ldc = problem_.ldc;
|
||||
gemm_workspace_.configuration.ldd = problem_.ldc;
|
||||
gemm_workspace_.configuration.split_k_slices = int(problem_.split_k_slices);
|
||||
|
||||
gemm_workspace_.arguments.A = nullptr;
|
||||
gemm_workspace_.arguments.B = nullptr;
|
||||
gemm_workspace_.arguments.C = nullptr;
|
||||
gemm_workspace_.arguments.D = nullptr;
|
||||
gemm_workspace_.arguments.alpha = problem_.alpha.data();
|
||||
gemm_workspace_.arguments.beta = problem_.beta.data();
|
||||
gemm_workspace_.arguments.pointer_mode = library::ScalarPointerMode::kHost;
|
||||
|
||||
initialize_result_(this->model_result_, options, operation_desc, problem_space);
|
||||
|
||||
return operation->can_implement(&gemm_workspace_.configuration, &gemm_workspace_.arguments);
|
||||
}
|
||||
|
||||
/// Initializes the performance result
|
||||
void GemmOperationProfiler::initialize_result_(
|
||||
PerformanceResult &result,
|
||||
Options const &options,
|
||||
library::GemmDescription const &operation_desc,
|
||||
ProblemSpace const &problem_space) {
|
||||
|
||||
result.provider = Provider::kCUTLASS;
|
||||
result.disposition = Disposition::kNotRun;
|
||||
result.status = Status::kSuccess;
|
||||
result.operation_name = operation_desc.name;
|
||||
|
||||
result.arguments.resize(problem_space.rank());
|
||||
|
||||
set_argument_(result, "A", problem_space,
|
||||
std::string(library::to_string(operation_desc.A.element)) + ":" + library::to_string(operation_desc.A.layout));
|
||||
|
||||
set_argument_(result, "B", problem_space,
|
||||
std::string(library::to_string(operation_desc.B.element)) + ":" + library::to_string(operation_desc.B.layout));
|
||||
|
||||
set_argument_(result, "C", problem_space,
|
||||
std::string(library::to_string(operation_desc.C.element)) + ":" + library::to_string(operation_desc.C.layout));
|
||||
|
||||
set_argument_(result, "m", problem_space, problem_.m);
|
||||
set_argument_(result, "n", problem_space, problem_.n);
|
||||
set_argument_(result, "k", problem_space, problem_.k);
|
||||
|
||||
set_argument_(result, "split_k_slices", problem_space, problem_.split_k_slices);
|
||||
set_argument_(result, "batch_count", problem_space, problem_.batch_count);
|
||||
|
||||
set_argument_(result, "alpha", problem_space,
|
||||
library::lexical_cast(problem_.alpha, operation_desc.element_epilogue));
|
||||
|
||||
set_argument_(result, "beta", problem_space,
|
||||
library::lexical_cast(problem_.beta, operation_desc.element_epilogue));
|
||||
|
||||
OperationProfiler::initialize_result_(result, operation_desc, problem_space);
|
||||
|
||||
result.bytes =
|
||||
int64_t(library::sizeof_bits(operation_desc.A.element) * problem_.m / 8) * problem_.k +
|
||||
int64_t(library::sizeof_bits(operation_desc.B.element) * problem_.n / 8) * problem_.k +
|
||||
int64_t(library::sizeof_bits(operation_desc.C.element) * problem_.m / 8) * problem_.n * 2;
|
||||
|
||||
result.flops = 2 * (problem_.m * problem_.n * problem_.k + problem_.m * problem_.n);
|
||||
|
||||
result.runtime = 0;
|
||||
|
||||
}
|
||||
|
||||
/// Initializes workspace
|
||||
Status GemmOperationProfiler::initialize_workspace(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
library::GemmDescription const &operation_desc =
|
||||
static_cast<library::GemmDescription const &>(operation->description());
|
||||
|
||||
if (options.execution_mode != ExecutionMode::kDryRun) {
|
||||
|
||||
gemm_workspace_.A = device_context.allocate_tensor(
|
||||
options,
|
||||
"A",
|
||||
operation_desc.A.element,
|
||||
operation_desc.A.layout,
|
||||
{int(problem_.m), int(problem_.k)},
|
||||
{int(problem_.lda)}
|
||||
);
|
||||
|
||||
gemm_workspace_.B = device_context.allocate_tensor(
|
||||
options,
|
||||
"B",
|
||||
operation_desc.B.element,
|
||||
operation_desc.B.layout,
|
||||
{int(problem_.k), int(problem_.n)},
|
||||
{int(problem_.ldb)}
|
||||
);
|
||||
|
||||
gemm_workspace_.C = device_context.allocate_tensor(
|
||||
options,
|
||||
"C",
|
||||
operation_desc.C.element,
|
||||
operation_desc.C.layout,
|
||||
{int(problem_.m), int(problem_.n)},
|
||||
{int(problem_.ldc)}
|
||||
);
|
||||
|
||||
gemm_workspace_.Computed = device_context.allocate_tensor(
|
||||
"D",
|
||||
operation_desc.C.element,
|
||||
operation_desc.C.layout,
|
||||
{int(problem_.m), int(problem_.n)},
|
||||
{int(problem_.ldc)}
|
||||
);
|
||||
|
||||
gemm_workspace_.Reference = device_context.allocate_tensor(
|
||||
"Reference",
|
||||
operation_desc.C.element,
|
||||
operation_desc.C.layout,
|
||||
{int(problem_.m), int(problem_.n)},
|
||||
{int(problem_.ldc)}
|
||||
);
|
||||
|
||||
gemm_workspace_.Reference->copy_from_device(gemm_workspace_.C->data());
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize the CUTLASS operation
|
||||
//
|
||||
Status status = Status::kSuccess;
|
||||
|
||||
if (options.profiling.provider_enabled(Provider::kCUTLASS)) {
|
||||
|
||||
if (options.execution_mode != ExecutionMode::kDryRun) {
|
||||
|
||||
uint64_t workspace_size = operation->get_host_workspace_size(&gemm_workspace_.configuration);
|
||||
gemm_workspace_.host_workspace.resize(workspace_size, 0);
|
||||
|
||||
workspace_size = operation->get_device_workspace_size(&gemm_workspace_.configuration);
|
||||
gemm_workspace_.device_workspace.reset(library::NumericTypeID::kU8, workspace_size);
|
||||
|
||||
status = operation->initialize(
|
||||
&gemm_workspace_.configuration,
|
||||
gemm_workspace_.host_workspace.data(),
|
||||
gemm_workspace_.device_workspace.data());
|
||||
}
|
||||
|
||||
//
|
||||
// If CUTLASS is enabled, generate a result for it
|
||||
//
|
||||
results_.push_back(model_result_);
|
||||
results_.back().provider = Provider::kCUTLASS;
|
||||
results_.back().disposition = Disposition::kNotRun;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Verifies CUTLASS against references
|
||||
bool GemmOperationProfiler::verify_cutlass(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
if (!options.profiling.provider_enabled(Provider::kCUTLASS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.execution_mode == ExecutionMode::kDryRun) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize structure containing GEMM arguments
|
||||
gemm_workspace_.arguments.A = gemm_workspace_.A->data();
|
||||
gemm_workspace_.arguments.B = gemm_workspace_.B->data();
|
||||
gemm_workspace_.arguments.C = gemm_workspace_.C->data();
|
||||
gemm_workspace_.arguments.D = gemm_workspace_.Computed->data();
|
||||
gemm_workspace_.arguments.alpha = problem_.alpha.data();
|
||||
gemm_workspace_.arguments.beta = problem_.beta.data();
|
||||
gemm_workspace_.arguments.pointer_mode = library::ScalarPointerMode::kHost;
|
||||
|
||||
//
|
||||
// Run the CUTLASS operation
|
||||
//
|
||||
|
||||
results_.back().status = operation->run(
|
||||
&gemm_workspace_.arguments,
|
||||
gemm_workspace_.host_workspace.data(),
|
||||
gemm_workspace_.device_workspace.data());
|
||||
|
||||
if (results_.back().status != Status::kSuccess) {
|
||||
results_.back().disposition = Disposition::kFailed;
|
||||
return false;
|
||||
}
|
||||
|
||||
cudaError_t result = cudaDeviceSynchronize();
|
||||
if (result != cudaSuccess) {
|
||||
results_.back().disposition = Disposition::kFailed;
|
||||
return false;
|
||||
}
|
||||
|
||||
results_.back().disposition = Disposition::kNotVerified;
|
||||
|
||||
if (options.verification.enabled) {
|
||||
|
||||
#if CUTLASS_ENABLE_CUBLAS
|
||||
if (options.verification.provider_enabled(Provider::kCUBLAS)) {
|
||||
|
||||
// Guard against unsupported cases
|
||||
auto const & gemm_desc = static_cast<library::GemmDescription const &>(operation->description());
|
||||
|
||||
if (cublas_satisfies(gemm_desc) != Status::kSuccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return verify_with_cublas_(
|
||||
options,
|
||||
report,
|
||||
device_context,
|
||||
operation,
|
||||
problem_space,
|
||||
problem);
|
||||
}
|
||||
#endif // #if CUTLASS_ENABLE_CUBLAS
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if CUTLASS_ENABLE_CUBLAS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Selects one or more cuBLAS algorithms.
|
||||
static void select_cublas_algorithms(
|
||||
std::vector<cublasGemmAlgo_t> &algorithms,
|
||||
Options const &options,
|
||||
library::GemmDescription const &op_desc) {
|
||||
|
||||
library::OpcodeClassID const & opcode_class =
|
||||
op_desc.tile_description.math_instruction.opcode_class;
|
||||
|
||||
switch (options.library.algorithm_mode) {
|
||||
case AlgorithmMode::kMatching:
|
||||
{
|
||||
algorithms.push_back(get_cublas_gemm_algo(
|
||||
op_desc.tile_description.threadblock_shape.m(),
|
||||
op_desc.tile_description.threadblock_shape.n(),
|
||||
op_desc.tile_description.threadblock_shape.k(),
|
||||
opcode_class));
|
||||
break;
|
||||
}
|
||||
|
||||
case AlgorithmMode::kBest:
|
||||
{
|
||||
// Choose first enumerated mode. If none are enumerated, choose based on opcode class
|
||||
// and evaluate all of them.
|
||||
|
||||
if (options.library.algorithms.empty()) {
|
||||
// Enumerate all algorithms
|
||||
if (opcode_class == library::OpcodeClassID::kSimt) {
|
||||
|
||||
for (int algo = CUBLAS_GEMM_DEFAULT;
|
||||
algo <= CUBLAS_GEMM_ALGO23;
|
||||
++algo) {
|
||||
|
||||
algorithms.push_back(cublasGemmAlgo_t(algo));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
for (int algo = CUBLAS_GEMM_DEFAULT_TENSOR_OP;
|
||||
algo <= CUBLAS_GEMM_ALGO15_TENSOR_OP;
|
||||
++algo) {
|
||||
|
||||
algorithms.push_back(cublasGemmAlgo_t(algo));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Use the listed algorithms
|
||||
algorithms.reserve(options.library.algorithms.size());
|
||||
|
||||
for (int algo : options.library.algorithms) {
|
||||
algorithms.push_back(reinterpret_cast<cublasGemmAlgo_t const &>(algo));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AlgorithmMode::kDefault:
|
||||
{
|
||||
|
||||
// Use the library's default algorithm
|
||||
algorithms.push_back((opcode_class == library::OpcodeClassID::kSimt ?
|
||||
CUBLAS_GEMM_DEFAULT : CUBLAS_GEMM_DEFAULT_TENSOR_OP));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatcher to cublasGemmEx()
|
||||
struct cublasGemmExDispatcher {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
library::GemmConfiguration configuration;
|
||||
library::GemmArguments arguments;
|
||||
|
||||
cublasOperation_t trans_A;
|
||||
cublasOperation_t trans_B;
|
||||
cudaDataType_t data_type_A;
|
||||
cudaDataType_t data_type_B;
|
||||
cudaDataType_t data_type_C;
|
||||
cudaDataType_t compute_type;
|
||||
cublasGemmAlgo_t algo;
|
||||
Status status;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
cublasGemmExDispatcher(
|
||||
library::GemmDescription const &op_desc,
|
||||
library::GemmConfiguration configuration_,
|
||||
library::GemmArguments arguments_,
|
||||
cublasGemmAlgo_t algorithm = CUBLAS_GEMM_DFALT
|
||||
):
|
||||
configuration(configuration_), arguments(arguments_), algo(algorithm), status(Status::kSuccess) {
|
||||
|
||||
trans_A = get_cublas_transpose_operation(op_desc.A.layout);
|
||||
trans_B = get_cublas_transpose_operation(op_desc.B.layout);
|
||||
|
||||
bool good = true;
|
||||
good = (good && get_cublas_datatype(data_type_A, op_desc.A.element));
|
||||
good = (good && get_cublas_datatype(data_type_B, op_desc.B.element));
|
||||
good = (good && get_cublas_datatype(data_type_C, op_desc.C.element));
|
||||
|
||||
good = (good && get_cublas_datatype(
|
||||
compute_type,
|
||||
op_desc.tile_description.math_instruction.element_accumulator));
|
||||
|
||||
if (!good) {
|
||||
status = Status::kErrorNotSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes GEMM using these arguments
|
||||
cublasStatus_t operator()(cublasHandle_t handle) {
|
||||
|
||||
return cublasGemmEx(
|
||||
handle,
|
||||
trans_A,
|
||||
trans_B,
|
||||
configuration.problem_size.m(),
|
||||
configuration.problem_size.n(),
|
||||
configuration.problem_size.k(),
|
||||
arguments.alpha,
|
||||
arguments.A,
|
||||
data_type_A,
|
||||
int(configuration.lda),
|
||||
arguments.B,
|
||||
data_type_B,
|
||||
int(configuration.ldb),
|
||||
arguments.beta,
|
||||
arguments.D,
|
||||
data_type_C,
|
||||
int(configuration.ldc),
|
||||
compute_type,
|
||||
algo
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#endif // CUTLASS_ENABLE_CUBLAS
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Verifies CUTLASS against references
|
||||
bool GemmOperationProfiler::verify_with_cublas_(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
|
||||
#if CUTLASS_ENABLE_CUBLAS
|
||||
|
||||
library::GemmDescription const &gemm_desc =
|
||||
static_cast<library::GemmDescription const &>(operation->description());
|
||||
|
||||
CublasCreate handle;
|
||||
cublasStatus_t status = handle.get_cublas_create_status();
|
||||
|
||||
if (status != CUBLAS_STATUS_SUCCESS) {
|
||||
|
||||
results_.back().status = get_cutlass_status(status);
|
||||
results_.back().disposition = Disposition::kFailed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<cublasGemmAlgo_t> algorithms;
|
||||
|
||||
detail::select_cublas_algorithms(
|
||||
algorithms,
|
||||
options,
|
||||
gemm_desc);
|
||||
|
||||
if (algorithms.empty()) {
|
||||
// no algorithm selected
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize state
|
||||
//
|
||||
|
||||
try {
|
||||
|
||||
//
|
||||
// Construct dispatcher to cublasGemmEx()
|
||||
//
|
||||
|
||||
// Initialize structure containing GEMM arguments
|
||||
gemm_workspace_.arguments.A = gemm_workspace_.A->data();
|
||||
gemm_workspace_.arguments.B = gemm_workspace_.B->data();
|
||||
gemm_workspace_.arguments.C = gemm_workspace_.Reference->data();
|
||||
gemm_workspace_.arguments.D = gemm_workspace_.Reference->data();
|
||||
gemm_workspace_.arguments.alpha = problem_.alpha.data();
|
||||
gemm_workspace_.arguments.beta = problem_.beta.data();
|
||||
gemm_workspace_.arguments.pointer_mode = library::ScalarPointerMode::kHost;
|
||||
|
||||
detail::cublasGemmExDispatcher gemm_op(
|
||||
gemm_desc,
|
||||
gemm_workspace_.configuration,
|
||||
gemm_workspace_.arguments,
|
||||
algorithms.front()
|
||||
);
|
||||
|
||||
if (gemm_op.status != Status::kSuccess) {
|
||||
results_.back().disposition = Disposition::kNotVerified;
|
||||
return true;
|
||||
}
|
||||
|
||||
results_.back().status = Status::kSuccess;
|
||||
|
||||
status = gemm_op(handle);
|
||||
|
||||
// Handle errors
|
||||
if (status != CUBLAS_STATUS_SUCCESS) {
|
||||
results_.back().status = get_cutlass_status(status);
|
||||
results_.back().disposition = Disposition::kNotVerified;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Verify results
|
||||
//
|
||||
|
||||
results_.back().disposition = compare_tensors(
|
||||
options,
|
||||
*gemm_workspace_.Computed,
|
||||
*gemm_workspace_.Reference
|
||||
);
|
||||
|
||||
// Save workspace if incorrect
|
||||
if (options.verification.save_workspace == SaveWorkspace::kIncorrect &&
|
||||
results_.back().disposition == Disposition::kIncorrect) {
|
||||
|
||||
save_workspace(
|
||||
device_context,
|
||||
options,
|
||||
gemm_desc,
|
||||
Provider::kCUTLASS,
|
||||
Provider::kCUBLAS);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
results_.back().disposition = Disposition::kFailed;
|
||||
results_.back().status = Status::kErrorNotSupported;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Return true means continue profiling
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Measures performance results
|
||||
bool GemmOperationProfiler::profile(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
if (options.profiling.provider_enabled(Provider::kCUTLASS)) {
|
||||
|
||||
// Initialize structure containing GEMM arguments
|
||||
gemm_workspace_.arguments.A = gemm_workspace_.A->data();
|
||||
gemm_workspace_.arguments.B = gemm_workspace_.B->data();
|
||||
gemm_workspace_.arguments.C = gemm_workspace_.C->data();
|
||||
gemm_workspace_.arguments.D = gemm_workspace_.Computed->data();
|
||||
gemm_workspace_.arguments.alpha = problem_.alpha.data();
|
||||
gemm_workspace_.arguments.beta = problem_.beta.data();
|
||||
gemm_workspace_.arguments.pointer_mode = library::ScalarPointerMode::kHost;
|
||||
|
||||
results_.back().status = profile_cutlass_(
|
||||
results_.back().runtime,
|
||||
options,
|
||||
operation,
|
||||
&gemm_workspace_.arguments,
|
||||
gemm_workspace_.host_workspace.data(),
|
||||
gemm_workspace_.device_workspace.data()
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,197 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
// CUTLASS Library includes
|
||||
#include "cutlass/library/library.h"
|
||||
#include "cutlass/library/manifest.h"
|
||||
|
||||
// Profiler includes
|
||||
#include "options.h"
|
||||
#include "device_context.h"
|
||||
#include "operation_profiler.h"
|
||||
#include "performance_result.h"
|
||||
#include "problem_space.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Abstract base class for each math function
|
||||
class GemmOperationProfiler : public OperationProfiler {
|
||||
public:
|
||||
|
||||
/// Problem structure obtained from problem space
|
||||
struct GemmProblem {
|
||||
int64_t m;
|
||||
int64_t n;
|
||||
int64_t k;
|
||||
int64_t lda;
|
||||
int64_t ldb;
|
||||
int64_t ldc;
|
||||
std::vector<uint8_t> alpha;
|
||||
std::vector<uint8_t> beta;
|
||||
int64_t split_k_slices;
|
||||
int64_t batch_count;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
GemmProblem():
|
||||
m(16), n(16), k(16), lda(0), ldb(0), ldc(0), split_k_slices(1), batch_count(1) { }
|
||||
};
|
||||
|
||||
/// Workspace used
|
||||
struct GemmWorkspace {
|
||||
|
||||
DeviceAllocation *A;
|
||||
DeviceAllocation *B;
|
||||
DeviceAllocation *C;
|
||||
DeviceAllocation *Computed;
|
||||
DeviceAllocation *Reference;
|
||||
|
||||
library::GemmConfiguration configuration;
|
||||
library::GemmArguments arguments;
|
||||
|
||||
/// Buffer used for the operation's host workspace
|
||||
std::vector<uint8_t> host_workspace;
|
||||
|
||||
/// Buffer used for the operations' device workspace
|
||||
DeviceAllocation device_workspace;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
GemmWorkspace():
|
||||
A(nullptr), B(nullptr), C(nullptr), Computed(nullptr), Reference(nullptr) { }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// GEMM problem obtained from problem space
|
||||
GemmProblem problem_;
|
||||
|
||||
/// Device memory allocations
|
||||
GemmWorkspace gemm_workspace_;
|
||||
|
||||
|
||||
public:
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Ctor
|
||||
GemmOperationProfiler();
|
||||
|
||||
/// Destructor
|
||||
virtual ~GemmOperationProfiler();
|
||||
|
||||
/// Prints usage statement for the math function
|
||||
virtual void print_usage(std::ostream &out) const;
|
||||
|
||||
/// Prints examples
|
||||
virtual void print_examples(std::ostream &out) const;
|
||||
|
||||
/// Extracts the problem dimensions
|
||||
virtual Status initialize_configuration(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Initializes workspace
|
||||
virtual Status initialize_workspace(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Verifies CUTLASS against references
|
||||
virtual bool verify_cutlass(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Measures performance results
|
||||
virtual bool profile(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
protected:
|
||||
|
||||
/// Initializes the performance result
|
||||
void initialize_result_(
|
||||
PerformanceResult &result,
|
||||
Options const &options,
|
||||
library::GemmDescription const &operation_desc,
|
||||
ProblemSpace const &problem_space);
|
||||
|
||||
/// Verifies CUTLASS against references
|
||||
bool verify_with_cublas_(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "gpu_timer.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GpuTimer::GpuTimer() {
|
||||
cudaError_t result;
|
||||
|
||||
for (auto & event : events) {
|
||||
result = cudaEventCreate(&event);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to create CUDA event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GpuTimer::~GpuTimer() {
|
||||
for (auto & event : events) {
|
||||
cudaEventDestroy(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Records a start event in the stream
|
||||
void GpuTimer::start(cudaStream_t stream) {
|
||||
cudaError_t result = cudaEventRecord(events[0], stream);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to record start event.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Records a stop event in the stream
|
||||
void GpuTimer::stop(cudaStream_t stream) {
|
||||
cudaError_t result = cudaEventRecord(events[1], stream);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to record stop event.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Records a stop event in the stream and synchronizes on the stream
|
||||
void GpuTimer::stop_and_wait(cudaStream_t stream) {
|
||||
|
||||
stop(stream);
|
||||
|
||||
cudaError_t result;
|
||||
if (stream) {
|
||||
result = cudaStreamSynchronize(stream);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to synchronize with non-null CUDA stream.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = cudaDeviceSynchronize();
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to synchronize with CUDA device.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the duration in miliseconds
|
||||
double GpuTimer::duration(int iterations) const {
|
||||
|
||||
float avg_ms;
|
||||
|
||||
cudaError_t result = cudaEventElapsedTime(&avg_ms, events[0], events[1]);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("Failed to query elapsed time from CUDA events.");
|
||||
}
|
||||
|
||||
return double(avg_ms) / double(iterations);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,65 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct GpuTimer {
|
||||
|
||||
cudaEvent_t events[2];
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
GpuTimer();
|
||||
~GpuTimer();
|
||||
|
||||
/// Records a start event in the stream
|
||||
void start(cudaStream_t stream = nullptr);
|
||||
|
||||
/// Records a stop event in the stream
|
||||
void stop(cudaStream_t stream = nullptr);
|
||||
|
||||
/// Records a stop event in the stream and synchronizes on the stream
|
||||
void stop_and_wait(cudaStream_t stream = nullptr);
|
||||
|
||||
/// Returns the duration in miliseconds
|
||||
double duration(int iterations = 1) const;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,47 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "options.h"
|
||||
|
||||
#include "cutlass_profiler.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int main(int argc, char const *arg[]) {
|
||||
|
||||
cutlass::CommandLine cmdline(argc, arg);
|
||||
cutlass::profiler::Options options(cmdline);
|
||||
|
||||
cutlass::profiler::CutlassProfiler profiler(options);
|
||||
|
||||
return profiler();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,595 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
// sleep not supported
|
||||
#endif
|
||||
|
||||
#include "options.h"
|
||||
#include "operation_profiler.h"
|
||||
#include "gpu_timer.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OperationProfiler::OperationProfiler(): kind_(library::OperationKind::kInvalid) { }
|
||||
|
||||
/// Ctor
|
||||
OperationProfiler::OperationProfiler(
|
||||
library::OperationKind kind,
|
||||
ArgumentDescriptionVector const &arguments,
|
||||
ProviderVector const & reference_providers
|
||||
):
|
||||
kind_(kind), arguments_(arguments), reference_providers_(reference_providers) {
|
||||
|
||||
ArgumentDescriptionVector tile_description_arguments{
|
||||
{ArgumentTypeID::kEnumerated, {"op_class", "opcode-class"}, "Class of math instruction (SIMT or TensorOp)."},
|
||||
{ArgumentTypeID::kEnumerated, {"accum", "accumulator-type"}, "Math instruction accumulator data type."},
|
||||
{ArgumentTypeID::kInteger, {"cta_m", "threadblock-shape::m"}, "Threadblock shape in the M dimension."},
|
||||
{ArgumentTypeID::kInteger, {"cta_n", "threadblock-shape::n"}, "Threadblock shape in the N dimension."},
|
||||
{ArgumentTypeID::kInteger, {"cta_k", "threadblock-shape::k"}, "Threadblock shape in the K dimension."},
|
||||
{ArgumentTypeID::kInteger, {"stages", "threadblock-stages"}, "Number of stages of threadblock-scoped matrix multiply."},
|
||||
{ArgumentTypeID::kInteger, {"warps_m", "warp-count::m"}, "Number of warps within threadblock along the M dimension."},
|
||||
{ArgumentTypeID::kInteger, {"warps_n", "warp-count::n"}, "Number of warps within threadblock along the N dimension."},
|
||||
{ArgumentTypeID::kInteger, {"warps_k", "warp-count::k"}, "Number of warps within threadblock along the K dimension."},
|
||||
{ArgumentTypeID::kInteger, {"inst_m", "instruction-shape::m"}, "Math instruction shape in the M dimension."},
|
||||
{ArgumentTypeID::kInteger, {"inst_n", "instruction-shape::n"}, "Math instruction shape in the N dimension."},
|
||||
{ArgumentTypeID::kInteger, {"inst_k", "instruction-shape::k"}, "Math instruction shape in the K dimension."},
|
||||
{ArgumentTypeID::kInteger, {"min_cc", "minimum-compute-capability"}, "Minimum device compute capability."},
|
||||
{ArgumentTypeID::kInteger, {"max_cc", "maximum-compute-capability"}, "Maximum device compute capability."}
|
||||
};
|
||||
|
||||
arguments_.insert(arguments_.end(), tile_description_arguments.begin(), tile_description_arguments.end());
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
OperationProfiler::~OperationProfiler() {
|
||||
|
||||
}
|
||||
|
||||
/// Gets the schema description
|
||||
std::string const & OperationProfiler::description() const {
|
||||
return description_;
|
||||
}
|
||||
|
||||
/// Prints usage statement for the math function
|
||||
void OperationProfiler::print_usage(std::ostream &out) const {
|
||||
for (auto const & desc : arguments_) {
|
||||
|
||||
size_t const kAliasStart = 10;
|
||||
|
||||
size_t columns = 0;
|
||||
|
||||
std::string type_str = to_string(desc.type);
|
||||
columns += type_str.size();
|
||||
|
||||
out << " [" << type_str << "]";
|
||||
|
||||
if (columns < kAliasStart) {
|
||||
out << std::string(kAliasStart - columns, ' ');
|
||||
}
|
||||
|
||||
columns = 0;
|
||||
|
||||
int j = 0;
|
||||
for (auto const & alias : desc.aliases) {
|
||||
columns += alias.size() + (j ? 1 : 0) + 2;
|
||||
|
||||
out << (j++ ? "," : "") << "--" << alias;
|
||||
}
|
||||
|
||||
size_t const kTotalColumns = 50;
|
||||
|
||||
if (columns < kTotalColumns) {
|
||||
out << std::string(kTotalColumns - columns, ' ');
|
||||
}
|
||||
|
||||
out << desc.description << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if the current operation description satisfies the problem space
|
||||
bool OperationProfiler::satisfies(
|
||||
library::OperationDescription const &op_desc,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
library::OpcodeClassID opcode_class;
|
||||
if (arg_as_OpcodeClassID(opcode_class, "op_class", problem_space, problem)) {
|
||||
if (opcode_class != op_desc.tile_description.math_instruction.opcode_class) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t int_value;
|
||||
|
||||
if (arg_as_int(int_value, "inst_m", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.m()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "inst_n", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.n()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "inst_k", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.k()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "cta_m", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.threadblock_shape.m()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "cta_n", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.threadblock_shape.n()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "cta_k", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.threadblock_shape.k()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "stages", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.threadblock_stages) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "warps_m", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.warp_count.m()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "warps_n", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.warp_count.n()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_as_int(int_value, "warps_k", problem_space, problem)) {
|
||||
if (int64_t(op_desc.tile_description.warp_count.k()) != int_value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
library::NumericTypeID numeric_type;
|
||||
if (arg_as_NumericTypeID(numeric_type, "accum", problem_space, problem)) {
|
||||
if (numeric_type != op_desc.tile_description.math_instruction.element_accumulator) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Entry point to profile all operations in the manifest
|
||||
int OperationProfiler::profile_all(
|
||||
Options const &options,
|
||||
library::Manifest const &manifest,
|
||||
DeviceContext &device_context) {
|
||||
|
||||
ProblemSpace problem_space(arguments_, options.cmdline);
|
||||
|
||||
// 1. Construct performance report
|
||||
PerformanceReport report(options, problem_space.argument_names());
|
||||
|
||||
// 2. For each problem in problem space
|
||||
ProblemSpace::Iterator problem_it = problem_space.begin();
|
||||
ProblemSpace::Iterator problem_end = problem_space.end();
|
||||
|
||||
bool continue_profiling = true;
|
||||
|
||||
// For each problem in problem space
|
||||
for (; continue_profiling && problem_it != problem_end; ++problem_it) {
|
||||
|
||||
ProblemSpace::Problem problem = problem_it.at();
|
||||
|
||||
report.next_problem();
|
||||
|
||||
// For each operation in manifest
|
||||
for (auto const & operation_ptr : manifest) {
|
||||
|
||||
library::Operation const *operation = operation_ptr.get();
|
||||
|
||||
auto min_cc = operation->description().tile_description.minimum_compute_capability;
|
||||
auto max_cc = operation->description().tile_description.maximum_compute_capability;
|
||||
|
||||
// Execute compatible operations if they satisfy the current device's compute capability
|
||||
if (operation->description().kind == kind_ &&
|
||||
options.device.compute_capability() >= min_cc &&
|
||||
options.device.compute_capability() <= max_cc) {
|
||||
|
||||
std::string operation_name(operation->description().name);
|
||||
|
||||
bool filtered_by_name = options.operation_names.empty();
|
||||
if (!filtered_by_name) {
|
||||
|
||||
for (auto const & op_name : options.operation_names) {
|
||||
if (operation_name.find(op_name) !=std::string::npos) {
|
||||
filtered_by_name = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered_by_name || !satisfies(operation->description(), problem_space, problem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A. Initialize configuration
|
||||
Status status = this->initialize_configuration(
|
||||
options,
|
||||
report,
|
||||
device_context,
|
||||
operation,
|
||||
problem_space,
|
||||
problem);
|
||||
|
||||
if (status == Status::kErrorInternal) {
|
||||
// Stop profiling if there was an internal error
|
||||
return false;
|
||||
}
|
||||
else if (status != Status::kSuccess) {
|
||||
// If the workspace could not be initialized for any other reason, continue to
|
||||
// the next operation.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (continue_profiling) {
|
||||
|
||||
status = this->initialize_workspace(
|
||||
options,
|
||||
report,
|
||||
device_context,
|
||||
operation,
|
||||
problem_space,
|
||||
problem);
|
||||
|
||||
if (status == Status::kErrorInternal) {
|
||||
// Stop profiling if there was an internal error
|
||||
return false;
|
||||
}
|
||||
else if (status != Status::kSuccess) {
|
||||
// If the workspace could not be initialized for any other reason, continue to
|
||||
// the next operation.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Profile CUTLASS if it is enabled
|
||||
//
|
||||
|
||||
// B. Verify CUTLASS
|
||||
if (continue_profiling) {
|
||||
|
||||
continue_profiling = this->verify_cutlass(
|
||||
options,
|
||||
report,
|
||||
device_context,
|
||||
operation,
|
||||
problem_space,
|
||||
problem);
|
||||
}
|
||||
|
||||
if (options.execution_mode == ExecutionMode::kDryRun) {
|
||||
report.append_results(results_);
|
||||
results_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// C. Optionally save workspace
|
||||
//
|
||||
|
||||
if (options.verification.save_workspace == SaveWorkspace::kAlways) {
|
||||
save_workspace(
|
||||
device_context,
|
||||
options,
|
||||
operation->description(),
|
||||
Provider::kCUTLASS);
|
||||
}
|
||||
|
||||
//
|
||||
// D. Profile
|
||||
//
|
||||
if (continue_profiling && options.profiling.enabled) {
|
||||
|
||||
continue_profiling = this->profile(
|
||||
options,
|
||||
report,
|
||||
device_context,
|
||||
operation,
|
||||
problem_space,
|
||||
problem);
|
||||
}
|
||||
|
||||
// Clear named allocations
|
||||
device_context.free();
|
||||
|
||||
report.append_results(results_);
|
||||
results_.clear();
|
||||
}
|
||||
|
||||
if (!continue_profiling) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Emit report
|
||||
report.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Sleep for a given duration in ms
|
||||
void OperationProfiler::sleep(int sleep_duration) {
|
||||
if (sleep_duration) {
|
||||
#ifdef __unix__
|
||||
usleep(sleep_duration * 1000);
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
SleepEx(sleep_duration, false);
|
||||
#else
|
||||
// sleep not supported
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Compares tensors for equality
|
||||
Disposition OperationProfiler::compare_tensors(
|
||||
Options const &options,
|
||||
DeviceAllocation &experimental,
|
||||
DeviceAllocation &reference) {
|
||||
|
||||
if (experimental.type() != reference.type()) {
|
||||
return Disposition::kIncorrect;
|
||||
}
|
||||
|
||||
bool passed = false;
|
||||
|
||||
if (options.verification.epsilon == 0) {
|
||||
|
||||
// bit-level equality
|
||||
passed = DeviceAllocation::block_compare_equal(
|
||||
experimental.type(),
|
||||
experimental.data(),
|
||||
reference.data(),
|
||||
experimental.capacity());
|
||||
}
|
||||
else {
|
||||
|
||||
// relative error function
|
||||
passed = DeviceAllocation::block_compare_relatively_equal(
|
||||
experimental.type(),
|
||||
experimental.data(),
|
||||
reference.data(),
|
||||
experimental.capacity(),
|
||||
options.verification.epsilon,
|
||||
options.verification.nonzero_floor);
|
||||
}
|
||||
|
||||
return passed ? Disposition::kPassed : Disposition::kIncorrect;
|
||||
}
|
||||
|
||||
/// Saves the workspace
|
||||
void OperationProfiler::save_workspace(
|
||||
DeviceContext &device_context,
|
||||
Options const &options,
|
||||
library::OperationDescription const &desc,
|
||||
Provider provider,
|
||||
Provider verification_provider) {
|
||||
|
||||
for (auto const & named_allocation : device_context) {
|
||||
|
||||
DeviceAllocation *allocation = named_allocation.second;
|
||||
|
||||
std::stringstream filename;
|
||||
|
||||
filename << desc.name << "_" << to_string(provider) << "_";
|
||||
|
||||
if (verification_provider != Provider::kInvalid) {
|
||||
filename << "verified_by_" << to_string(verification_provider) << "_";
|
||||
}
|
||||
|
||||
filename << named_allocation.first + ".mat";
|
||||
|
||||
std::ofstream out(filename.str());
|
||||
|
||||
allocation->write_tensor_csv(out);
|
||||
|
||||
if (options.report.verbose) {
|
||||
std::cout << "wrote '" << filename.str() << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Method to profile a CUTLASS Operation
|
||||
Status OperationProfiler::profile_cutlass_(
|
||||
double &runtime,
|
||||
Options const &options,
|
||||
library::Operation const *operation,
|
||||
void const *arguments,
|
||||
void *host_workspace,
|
||||
void *device_workspace) {
|
||||
|
||||
GpuTimer timer;
|
||||
|
||||
//
|
||||
// Optional sleep to limit power consumption and thermals
|
||||
//
|
||||
|
||||
sleep(options.profiling.sleep_duration);
|
||||
|
||||
//
|
||||
// Warmup loop
|
||||
//
|
||||
|
||||
Status status;
|
||||
|
||||
for (int iteration = 0; iteration < options.profiling.warmup_iterations; ++iteration) {
|
||||
|
||||
status = operation->run(
|
||||
arguments,
|
||||
host_workspace,
|
||||
device_workspace);
|
||||
|
||||
if (status != Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize GPU timer
|
||||
//
|
||||
|
||||
timer.start();
|
||||
|
||||
//
|
||||
// Profiling loop
|
||||
//
|
||||
|
||||
int Iterations = options.profiling.iterations;
|
||||
|
||||
int iteration = 0;
|
||||
for (; iteration < Iterations; ++iteration) {
|
||||
|
||||
status = operation->run(
|
||||
arguments,
|
||||
host_workspace,
|
||||
device_workspace);
|
||||
|
||||
if (status != Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Wait for completion
|
||||
//
|
||||
|
||||
timer.stop_and_wait();
|
||||
|
||||
//
|
||||
// Update performance result
|
||||
//
|
||||
|
||||
runtime = timer.duration(iteration);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Sets operation description
|
||||
void OperationProfiler::initialize_result_(
|
||||
PerformanceResult &result,
|
||||
library::OperationDescription const &operation_desc,
|
||||
ProblemSpace const &problem_space) {
|
||||
|
||||
set_argument_(result, "op_class", problem_space,
|
||||
library::to_string(operation_desc.tile_description.math_instruction.opcode_class));
|
||||
|
||||
set_argument_(result, "accum", problem_space,
|
||||
library::to_string(operation_desc.tile_description.math_instruction.element_accumulator));
|
||||
|
||||
set_argument_(result, "cta_m", problem_space, operation_desc.tile_description.threadblock_shape.m());
|
||||
set_argument_(result, "cta_n", problem_space, operation_desc.tile_description.threadblock_shape.n());
|
||||
set_argument_(result, "cta_k", problem_space, operation_desc.tile_description.threadblock_shape.k());
|
||||
set_argument_(result, "stages", problem_space, operation_desc.tile_description.threadblock_stages);
|
||||
set_argument_(result, "warps_m", problem_space, operation_desc.tile_description.warp_count.m());
|
||||
set_argument_(result, "warps_n", problem_space, operation_desc.tile_description.warp_count.n());
|
||||
set_argument_(result, "warps_k", problem_space, operation_desc.tile_description.warp_count.k());
|
||||
set_argument_(result, "inst_m", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.m());
|
||||
set_argument_(result, "inst_n", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.n());
|
||||
set_argument_(result, "inst_k", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.k());
|
||||
set_argument_(result, "min_cc", problem_space, operation_desc.tile_description.minimum_compute_capability);
|
||||
set_argument_(result, "max_cc", problem_space, operation_desc.tile_description.maximum_compute_capability);
|
||||
}
|
||||
|
||||
|
||||
/// Helper
|
||||
void OperationProfiler::set_argument_(
|
||||
PerformanceResult &result,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
std::string const &value) {
|
||||
|
||||
result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), value);
|
||||
}
|
||||
|
||||
void OperationProfiler::set_argument_(
|
||||
PerformanceResult &result,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
int64_t value) {
|
||||
|
||||
result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), library::lexical_cast(value));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,240 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
// CUTLASS Library includes
|
||||
#include "cutlass/library/library.h"
|
||||
#include "cutlass/library/manifest.h"
|
||||
|
||||
// Profiler includes
|
||||
#include "options.h"
|
||||
#include "device_context.h"
|
||||
#include "performance_result.h"
|
||||
#include "performance_report.h"
|
||||
#include "problem_space.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Abstract base class for each math function
|
||||
class OperationProfiler {
|
||||
public:
|
||||
|
||||
|
||||
protected:
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Top-level operation kind
|
||||
library::OperationKind kind_;
|
||||
|
||||
/// Human readable description
|
||||
std::string description_;
|
||||
|
||||
/// Arguments parsed from command line
|
||||
ArgumentDescriptionVector arguments_;
|
||||
|
||||
/// List of providers used to verify and compare each result
|
||||
ProviderVector reference_providers_;
|
||||
|
||||
/// Model performance result initailized by the operation profiler with workload statistics
|
||||
/// and reasonable default state.
|
||||
PerformanceResult model_result_;
|
||||
|
||||
/// Performance result vector constructed by profiling the operation
|
||||
PerformanceResultVector results_;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Ctor
|
||||
OperationProfiler();
|
||||
|
||||
OperationProfiler(
|
||||
library::OperationKind kind,
|
||||
ArgumentDescriptionVector const &arguments = ArgumentDescriptionVector(),
|
||||
ProviderVector const & reference_providers = ProviderVector());
|
||||
|
||||
/// Destructor
|
||||
virtual ~OperationProfiler();
|
||||
|
||||
/// Obtains the operation kind
|
||||
library::OperationKind kind() const { return kind_; }
|
||||
|
||||
/// Gets the schema description
|
||||
std::string const &description() const;
|
||||
|
||||
/// Returns a reference to the arguments
|
||||
ArgumentDescriptionVector const &arguments() const { return arguments_; }
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Basic overrides
|
||||
//
|
||||
|
||||
|
||||
/// Prints usage statement for the math function
|
||||
virtual void print_usage(std::ostream &out) const;
|
||||
|
||||
/// Prints examples
|
||||
virtual void print_examples(std::ostream &out) const =0;
|
||||
|
||||
/// Entry point to profile all operations in the manifest
|
||||
virtual int profile_all(
|
||||
Options const &options,
|
||||
library::Manifest const &manifest,
|
||||
DeviceContext &device_context);
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Operation-specific phases of verification and profiling
|
||||
//
|
||||
|
||||
/// Extracts the problem dimensions
|
||||
virtual Status initialize_configuration(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) = 0;
|
||||
|
||||
/// Initializes workspace
|
||||
virtual Status initialize_workspace(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) = 0;
|
||||
|
||||
/// Verifies CUTLASS against references
|
||||
virtual bool verify_cutlass(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) = 0;
|
||||
|
||||
/// Measures performance results
|
||||
virtual bool profile(
|
||||
Options const &options,
|
||||
PerformanceReport &report,
|
||||
DeviceContext &device_context,
|
||||
library::Operation const *operation,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) = 0;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Static helpers
|
||||
//
|
||||
|
||||
/// Sleep for a given duration in ms
|
||||
static void sleep(int sleep_duration);
|
||||
|
||||
/// Returns true if the current operation description satisfies the problem space
|
||||
static bool satisfies(
|
||||
library::OperationDescription const &op_desc,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Compares tensors for equality
|
||||
static Disposition compare_tensors(
|
||||
Options const &options,
|
||||
DeviceAllocation &experimental,
|
||||
DeviceAllocation &reference);
|
||||
|
||||
static void save_workspace(
|
||||
DeviceContext &device_context,
|
||||
Options const &options,
|
||||
library::OperationDescription const &desc,
|
||||
Provider provider,
|
||||
Provider verification_provider = Provider::kInvalid);
|
||||
|
||||
protected:
|
||||
|
||||
/// Sets operation description
|
||||
static void initialize_result_(
|
||||
PerformanceResult &result,
|
||||
library::OperationDescription const &operation_desc,
|
||||
ProblemSpace const &problem_space);
|
||||
|
||||
/// Helper to set a performance result member
|
||||
static void set_argument_(
|
||||
PerformanceResult &result,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
std::string const &value);
|
||||
|
||||
/// Helper to set a performance result member
|
||||
static void set_argument_(
|
||||
PerformanceResult &result,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
int64_t value);
|
||||
|
||||
/// Method to profile an initialized CUTLASS operation
|
||||
virtual Status profile_cutlass_(
|
||||
double &runtime,
|
||||
Options const &options,
|
||||
library::Operation const *operation,
|
||||
void const *arguments,
|
||||
void *host_workspace,
|
||||
void *device_workspace);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Vector of owning operation profilers
|
||||
using OperationProfilerVector = std::vector<std::unique_ptr<OperationProfiler>>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,750 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Command line options for performance test program
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/version.h"
|
||||
|
||||
#include "options.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Newline and indent for help strings
|
||||
static char const *end_of_line = "\n ";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Device::Device(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
cmdline.get_cmd_line_argument("device", device, 0);
|
||||
|
||||
cudaError_t result;
|
||||
result = cudaGetDeviceProperties(&properties, device);
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("cudaGetDeviceProperties() failed for given device");
|
||||
}
|
||||
|
||||
result = cudaSetDevice(device);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("cudaSetDevice() failed for given device.");
|
||||
}
|
||||
|
||||
// Permit overriding the compute capability
|
||||
if (cmdline.check_cmd_line_flag("compute-capability")) {
|
||||
int cc = compute_capability();
|
||||
cmdline.get_cmd_line_argument("compute-capability", cc, cc);
|
||||
properties.major = cc / 10;
|
||||
properties.minor = cc % 10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Options::Device::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Device:\n"
|
||||
<< " --device=<int> "
|
||||
<< " CUDA Device ID\n\n";
|
||||
|
||||
int device_count = 0;
|
||||
cudaError_t result = cudaGetDeviceCount(&device_count);
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
out << " <could not query for CUDA devices>\n";
|
||||
}
|
||||
else {
|
||||
|
||||
for (int idx = 0; idx < device_count; ++idx) {
|
||||
cudaDeviceProp prop;
|
||||
result = cudaGetDeviceProperties(&prop, idx);
|
||||
if (result != cudaSuccess) {
|
||||
out << " <could not obtain device properties for device " << idx << ">" << std::endl;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
out << " [" << idx << "] - "
|
||||
<< prop.name << " - SM " << prop.major << "." << prop.minor << ", "
|
||||
<< prop.multiProcessorCount << " SMs @ " << (prop.clockRate / 1000.0) << " MHz, "
|
||||
<< "L2 cache: " << (prop.l2CacheSize >> 20) << " MB, Global Memory: " << (prop.totalGlobalMem >> 30) << " GB"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
out
|
||||
<< " --compute-capability=<int> "
|
||||
<< " Override the compute capability.\n\n";
|
||||
|
||||
}
|
||||
|
||||
void Options::Device::print_device_info(std::ostream &out) const {
|
||||
int num_devices;
|
||||
cudaDeviceProp props;
|
||||
|
||||
cudaError_t result;
|
||||
result = cudaGetDeviceCount(&num_devices);
|
||||
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("cudaGetNumDevices() failed");
|
||||
}
|
||||
|
||||
out << "Device Name,SM,CUDA Device ID,Phy Device ID" << std::endl;
|
||||
|
||||
for(int device = 0; device < num_devices; device++) {
|
||||
result = cudaSetDevice(device);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("cudaSetDevice() failed for device");
|
||||
}
|
||||
|
||||
result = cudaGetDeviceProperties(&props, device);
|
||||
if (result != cudaSuccess) {
|
||||
throw std::runtime_error("cudaGetDeviceProperties failed for device");
|
||||
}
|
||||
|
||||
out << props.name << "," << props.major << props.minor << ","
|
||||
<< device << "," << props.multiGpuBoardGroupID << std::endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Options::Device::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "device: " << device << "\n"
|
||||
<< indent_str(indent) << "clock: " << int(double(properties.clockRate) / 1000.0) << "\n"
|
||||
<< indent_str(indent) << "compute-capability: " << compute_capability() << "\n";
|
||||
}
|
||||
|
||||
/// Returns the compute capability of the listed device (e.g. 61, 60, 70, 75)
|
||||
int Options::Device::compute_capability() const {
|
||||
return properties.major * 10 + properties.minor;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Initialization::Initialization(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
cmdline.get_cmd_line_argument("initialization-enabled", enabled, true);
|
||||
|
||||
if (cmdline.check_cmd_line_flag("initialization-provider")) {
|
||||
std::string str;
|
||||
cmdline.get_cmd_line_argument("initialization-provider", str);
|
||||
provider = from_string<Provider>(str);
|
||||
if (provider == Provider::kInvalid) {
|
||||
enabled = false;
|
||||
}
|
||||
else if (provider != Provider::kReferenceHost && provider != Provider::kReferenceDevice) {
|
||||
throw std::runtime_error("Unsupported intialization provider specified.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
provider = Provider::kReferenceDevice;
|
||||
}
|
||||
|
||||
cmdline.get_cmd_line_argument("seed", seed, 2019);
|
||||
|
||||
if (cmdline.check_cmd_line_flag("dist")) {
|
||||
get_distribution(cmdline, "dist", data_distribution);
|
||||
}
|
||||
else {
|
||||
data_distribution.set_uniform(-4, 4, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Gets the initial distribution
|
||||
void Options::Initialization::get_distribution(
|
||||
cutlass::CommandLine const &args,
|
||||
std::string const &arg,
|
||||
cutlass::Distribution &dist) {
|
||||
|
||||
struct {
|
||||
const char *label;
|
||||
cutlass::Distribution::Kind kind;
|
||||
} distribution_kinds[] = {
|
||||
{"uniform", cutlass::Distribution::Uniform},
|
||||
{"gaussian", cutlass::Distribution::Gaussian},
|
||||
{"identity", cutlass::Distribution::Identity},
|
||||
{"sequential", cutlass::Distribution::Sequential},
|
||||
{0, cutlass::Distribution::Invalid}
|
||||
};
|
||||
|
||||
struct {
|
||||
char const *label;
|
||||
double *member;
|
||||
} members[] = {
|
||||
{"min", &dist.uniform.min},
|
||||
{"max", &dist.uniform.max},
|
||||
{"mean", &dist.gaussian.mean},
|
||||
{"stddev", &dist.gaussian.stddev},
|
||||
{"start", &dist.sequential.start},
|
||||
{"delta", &dist.sequential.delta},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
using KeyValueVector = std::vector<std::pair<std::string, std::string> >;
|
||||
|
||||
KeyValueVector values;
|
||||
args.get_cmd_line_argument_pairs(arg.c_str(), values);
|
||||
|
||||
// The parser expects the first token to be a string identifying the distribution type.
|
||||
auto it = values.begin();
|
||||
if (it != values.end()) {
|
||||
for (int i = 0; distribution_kinds[i].label; ++i) {
|
||||
if (it->first == distribution_kinds[i].label) {
|
||||
dist.kind = distribution_kinds[i].kind;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// Subsequent key-value pairs update the named field of the distribution struct.
|
||||
for (; it != values.end(); ++it) {
|
||||
// Integer scaling factor - if < 0, no integer rounding is performed.
|
||||
if ((it->first.compare("scale") == 0) && !it->second.empty()) {
|
||||
std::stringstream ss;
|
||||
ss << it->second;
|
||||
ss >> dist.int_scale;
|
||||
continue; // next token
|
||||
}
|
||||
|
||||
// Casts as integer without scaling
|
||||
if (it->first.compare("integer") == 0) {
|
||||
dist.int_scale = 0;
|
||||
continue; // next token
|
||||
}
|
||||
|
||||
// Casts as integer without scaling
|
||||
if (it->first.compare("integer") == 0) {
|
||||
dist.int_scale = 0;
|
||||
continue; // next token
|
||||
}
|
||||
|
||||
// initialize other members
|
||||
for (int m = 0; members[m].label; ++m) {
|
||||
if (it->first == members[m].label && !it->second.empty()) {
|
||||
std::stringstream ss;
|
||||
ss << it->second;
|
||||
ss >> *(members[m].member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Options::Initialization::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Initialization:\n"
|
||||
|
||||
<< " --initialization=<bool> "
|
||||
<< " Enables initialization (default: true). If false, device memory is" << end_of_line
|
||||
<< "not initialized after allocation.\n\n"
|
||||
|
||||
<< " --initialization-provider=<provider> "
|
||||
<< " Selects 'device' or 'host' initialization.\n\n"
|
||||
|
||||
<< " --dist=<distribution> "
|
||||
<< " Data distribution of input tensors\n\n"
|
||||
|
||||
<< " --seed=<int> "
|
||||
<< " Random number generator seed. Used to enforce deterministic" << end_of_line
|
||||
<< "initialization.\n\n";
|
||||
|
||||
}
|
||||
|
||||
void Options::Initialization::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Library::Library(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
algorithm_mode = AlgorithmMode::kDefault;
|
||||
|
||||
if (cmdline.check_cmd_line_flag("library-algo-mode")) {
|
||||
std::string mode = "default";
|
||||
cmdline.get_cmd_line_argument("library-algo-mode", mode);
|
||||
algorithm_mode = from_string<AlgorithmMode>(mode);
|
||||
}
|
||||
|
||||
if (cmdline.check_cmd_line_flag("library-algos")) {
|
||||
|
||||
// If algorithms are specified, override as kBest.
|
||||
algorithm_mode = AlgorithmMode::kBest;
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
cmdline.get_cmd_line_arguments("library-algos", tokens);
|
||||
|
||||
algorithms.reserve(tokens.size());
|
||||
|
||||
for (auto const & token : tokens) {
|
||||
if (token.find(":")) {
|
||||
// todo - tokenized range
|
||||
}
|
||||
else {
|
||||
int algo;
|
||||
std::stringstream ss;
|
||||
|
||||
ss << token;
|
||||
ss >> algo;
|
||||
|
||||
algorithms.push_back(algo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Options::Library::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Library:\n"
|
||||
|
||||
<< " --library-algo-mode=<mode> "
|
||||
<< " Indicates algorithm mode used to call libraries such as cuBLAS and cuDNN.\n"
|
||||
<< " "
|
||||
<< " mode={default*,matching,best}\n\n"
|
||||
|
||||
<< " --library-algos=<range-list> "
|
||||
<< " If --algorithm-mode=best, permits specifying a selection of algorithms.\n\n";
|
||||
|
||||
}
|
||||
|
||||
void Options::Library::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "library-algo-mode: " << to_string(algorithm_mode) << "\n"
|
||||
<< indent_str(indent) << "library-algos: ";
|
||||
|
||||
int j = 0;
|
||||
for (int x : algorithms) {
|
||||
out << (j++ ? "," : "") << x;
|
||||
}
|
||||
|
||||
out << "\n\n";
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Profiling::Profiling(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
cmdline.get_cmd_line_argument("warmup-iterations", warmup_iterations, 10);
|
||||
cmdline.get_cmd_line_argument("profiling-iterations", iterations, 100);
|
||||
cmdline.get_cmd_line_argument("sleep-duration", sleep_duration, 50);
|
||||
cmdline.get_cmd_line_argument("profiling-enabled", enabled, true);
|
||||
|
||||
if (cmdline.check_cmd_line_flag("providers")) {
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
cmdline.get_cmd_line_arguments("providers", tokens);
|
||||
|
||||
providers.clear();
|
||||
|
||||
for (auto const &token : tokens) {
|
||||
providers.push_back(from_string<Provider>(token));
|
||||
}
|
||||
}
|
||||
else {
|
||||
providers.push_back(Provider::kCUTLASS);
|
||||
providers.push_back(Provider::kCUBLAS);
|
||||
}
|
||||
}
|
||||
|
||||
void Options::Profiling::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Profiling:\n"
|
||||
|
||||
<< " --profiling-iterations=<iterations> "
|
||||
<< " Number of iterations to profile each kernel. If zero, kernels" << end_of_line
|
||||
<< "are launched up to the profiling duration.\n\n"
|
||||
|
||||
<< " --warmup-iterations=<iterations> "
|
||||
<< " Number of iterations to execute each kernel prior to profiling.\n\n"
|
||||
|
||||
<< " --sleep-duration=<duration> "
|
||||
<< " Number of ms to sleep between profiling periods (ms)\n\n"
|
||||
|
||||
<< " --profiling-enabled=<bool> "
|
||||
<< " If true, profiling is actually conducted.\n\n"
|
||||
|
||||
<< " --providers=<providers> "
|
||||
<< " List of providers to be profiled for performance\n\n";
|
||||
}
|
||||
|
||||
void Options::Profiling::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "profiling_iterations: " << iterations << "\n"
|
||||
<< indent_str(indent) << "sleep_duration: " << sleep_duration << "\n"
|
||||
<< indent_str(indent) << "profiling_enabled: " << enabled << "\n"
|
||||
<< indent_str(indent) << "providers: [";
|
||||
|
||||
int j = 0;
|
||||
for (auto const & provider : providers) {
|
||||
out << (j++ ? ", " : "") << to_string(provider);
|
||||
}
|
||||
out << "]\n";
|
||||
}
|
||||
|
||||
/// Returns true if a provider is enabled
|
||||
bool Options::Profiling::provider_enabled(Provider provider) const {
|
||||
return std::find(providers.begin(), providers.end(), provider) != providers.end();
|
||||
}
|
||||
|
||||
/// Returns the index of a provider if its enabled
|
||||
size_t Options::Profiling::index(Provider provider) const {
|
||||
size_t idx = 0;
|
||||
for (auto const & x : providers) {
|
||||
if (x == provider) {
|
||||
return idx;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Verification::Verification(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
cmdline.get_cmd_line_argument("verification-enabled", enabled, true);
|
||||
|
||||
cmdline.get_cmd_line_argument("epsilon", epsilon, 0.05);
|
||||
|
||||
cmdline.get_cmd_line_argument("nonzero-floor", nonzero_floor, 1.0 / 256.0);
|
||||
|
||||
if (cmdline.check_cmd_line_flag("save-workspace")) {
|
||||
std::string value;
|
||||
cmdline.get_cmd_line_argument("save-workspace", value);
|
||||
save_workspace = from_string<SaveWorkspace>(value);
|
||||
}
|
||||
else {
|
||||
save_workspace = SaveWorkspace::kNever;
|
||||
}
|
||||
|
||||
if (cmdline.check_cmd_line_flag("verification-providers")) {
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
cmdline.get_cmd_line_arguments("verification-providers", tokens);
|
||||
|
||||
providers.clear();
|
||||
|
||||
for (auto const &token : tokens) {
|
||||
Provider provider = from_string<Provider>(token);
|
||||
if (provider != Provider::kInvalid) {
|
||||
providers.push_back(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
providers.push_back(Provider::kCUBLAS);
|
||||
}
|
||||
}
|
||||
|
||||
void Options::Verification::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Verification:\n"
|
||||
|
||||
<< " --verification-enabled=<bool> "
|
||||
<< " Whether to perform verification checks.\n\n"
|
||||
|
||||
<< " --epsilon=<error> "
|
||||
<< " Error threshold. Setting to zero (default) requires" << end_of_line
|
||||
<< "bit-level equivalence.\n\n"
|
||||
|
||||
<< " --nonzero-floor=<floor> "
|
||||
<< " Results whose absolute value is less than this quantity" << end_of_line
|
||||
<< "are treated as zero for comparisons.\n\n"
|
||||
|
||||
<< " --save-workspace={*never,incorrect,always}"
|
||||
<< " Specifies when to save the GEMM inputs and results to the filesystem.\n\n"
|
||||
|
||||
<< " --verification-providers=<providers> "
|
||||
<< " List of providers used to verify result. (default: device)\n\n";
|
||||
}
|
||||
|
||||
void Options::Verification::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "verification_enabled: " << enabled << "\n"
|
||||
<< indent_str(indent) << "epsilon: " << epsilon << "\n"
|
||||
<< indent_str(indent) << "save_workspace: " << to_string(save_workspace) << "\n"
|
||||
<< indent_str(indent) << "verification_providers: [";
|
||||
|
||||
int j = 0;
|
||||
for (auto const & provider : providers) {
|
||||
out << (j++ ? ", " : "") << to_string(provider);
|
||||
}
|
||||
out << "]\n";
|
||||
}
|
||||
|
||||
/// Returns true if a provider is enabled
|
||||
bool Options::Verification::provider_enabled(Provider provider) const {
|
||||
return std::find(providers.begin(), providers.end(), provider) != providers.end();
|
||||
}
|
||||
|
||||
/// Returns the index of a provider if its enabled
|
||||
size_t Options::Verification::index(Provider provider) const {
|
||||
size_t idx = 0;
|
||||
for (auto const & x : providers) {
|
||||
if (x == provider) {
|
||||
return idx;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Report::Report(cutlass::CommandLine const &cmdline) {
|
||||
|
||||
cmdline.get_cmd_line_argument("append", append, false);
|
||||
cmdline.get_cmd_line_argument("output", output_path);
|
||||
|
||||
if (cmdline.check_cmd_line_flag("tags")) {
|
||||
cmdline.get_cmd_line_argument_pairs("tags", pivot_tags);
|
||||
}
|
||||
|
||||
cmdline.get_cmd_line_argument("report-not-run", report_not_run, false);
|
||||
|
||||
cmdline.get_cmd_line_argument("verbose", verbose, true);
|
||||
}
|
||||
|
||||
void Options::Report::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "Report:\n"
|
||||
|
||||
<< " --append=<bool> "
|
||||
<< " If true, result is appended to possibly existing file. Otherwise, " << end_of_line
|
||||
<< "any existing file is overwritten.\n\n"
|
||||
|
||||
<< " --output=<path> "
|
||||
<< " Path to output file for machine readable results.\n\n"
|
||||
|
||||
<< " --report-not-run=<bool> "
|
||||
<< " If true, reports the status of all kernels including those that" << end_of_line
|
||||
<< "do not satisfy the given arguments.\n\n"
|
||||
|
||||
<< " --tags=<column:tag,...> "
|
||||
<< " Inserts leading columns in output table and uniform values for each" << end_of_line
|
||||
<< "column. Useful for generating pivot tables.\n\n"
|
||||
|
||||
<< " --verbose=<bool> "
|
||||
<< " Prints human-readable text to stdout. If false, nothing is written to stdout.\n\n";
|
||||
}
|
||||
|
||||
void Options::Report::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "append: " << append << "\n"
|
||||
<< indent_str(indent) << "output: " << output_path << "\n"
|
||||
<< indent_str(indent) << "report_not_run: " << report_not_run << "\n"
|
||||
<< indent_str(indent) << "tags:\n";
|
||||
|
||||
for (auto const & tag : pivot_tags) {
|
||||
out << indent_str(indent + 1) << tag.first << ": " << tag.second << "\n";
|
||||
}
|
||||
|
||||
out
|
||||
<< indent_str(indent) << "verbose: " << verbose << "\n";
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::About::About(cutlass::CommandLine const &cmdline) {
|
||||
help = cmdline.check_cmd_line_flag("help");
|
||||
version = cmdline.check_cmd_line_flag("version");
|
||||
device_info = cmdline.check_cmd_line_flag("device-info");
|
||||
}
|
||||
|
||||
void Options::About::print_usage(std::ostream &out) const {
|
||||
|
||||
out << "About:\n"
|
||||
<< " --version ";
|
||||
|
||||
print_version(out);
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void Options::About::print_version(std::ostream &out) {
|
||||
out << "CUTLASS " << cutlass::getVersionString()
|
||||
<< " built on " << __DATE__ << " at " << __TIME__;
|
||||
if (!cutlass::getGitRevision().empty()) out << " with commit " << cutlass::getGitRevision() << "";
|
||||
}
|
||||
|
||||
void Options::About::print_options(std::ostream &out, int indent) const {
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Options::Options(cutlass::CommandLine const &cmdline):
|
||||
cmdline(cmdline),
|
||||
device(cmdline),
|
||||
initialization(cmdline),
|
||||
library(cmdline),
|
||||
profiling(cmdline),
|
||||
verification(cmdline),
|
||||
report(cmdline),
|
||||
about(cmdline) {
|
||||
|
||||
if (cmdline.check_cmd_line_flag("mode")) {
|
||||
std::string token;
|
||||
cmdline.get_cmd_line_argument("mode", token);
|
||||
execution_mode = from_string<ExecutionMode>(token);
|
||||
}
|
||||
else {
|
||||
execution_mode = ExecutionMode::kProfile;
|
||||
}
|
||||
|
||||
// Enumerating kernels is equivalent to a dry run.
|
||||
if (execution_mode == ExecutionMode::kEnumerate) {
|
||||
execution_mode = ExecutionMode::kDryRun;
|
||||
}
|
||||
|
||||
if (cmdline.check_cmd_line_flag("operation")) {
|
||||
std::string str;
|
||||
cmdline.get_cmd_line_argument("operation", str);
|
||||
operation_kind = library::from_string<library::OperationKind>(str);
|
||||
}
|
||||
else if (cmdline.check_cmd_line_flag("function")) {
|
||||
std::string str;
|
||||
cmdline.get_cmd_line_argument("function", str);
|
||||
operation_kind = library::from_string<library::OperationKind>(str);
|
||||
}
|
||||
else {
|
||||
operation_kind = library::OperationKind::kInvalid;
|
||||
}
|
||||
|
||||
if (cmdline.check_cmd_line_flag("operation_names")) {
|
||||
cmdline.get_cmd_line_arguments("operation_names", operation_names);
|
||||
}
|
||||
else if (cmdline.check_cmd_line_flag("kernels")) {
|
||||
cmdline.get_cmd_line_arguments("kernels", operation_names);
|
||||
}
|
||||
|
||||
// Prevent launches on the device for anything other than CUTLASS operation
|
||||
if (execution_mode == ExecutionMode::kTrace) {
|
||||
initialization.provider = Provider::kReferenceHost;
|
||||
verification.enabled = false;
|
||||
profiling.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Options::print_usage(std::ostream &out) const {
|
||||
|
||||
out
|
||||
<< "CUTLASS Performance Tool\n"
|
||||
<< "usage:\n\n"
|
||||
<< " cutlass_profiler [options]\n\n"
|
||||
<< " --help\n\n"
|
||||
|
||||
<< " --mode={profile*,single,dry,trace,enumerate} "
|
||||
<< " Regular profiling, single kernel mode only, or no profiling.\n\n"
|
||||
|
||||
<< " --device-info "
|
||||
<< " Prints information on all GPUs present in the system\n\n"
|
||||
|
||||
<< " --operation=<operation_kind> "
|
||||
<< " CUTLASS operation to profile.\n\n"
|
||||
|
||||
<< " --kernels=<string_list> "
|
||||
<< " List of substrings to filter operations by name.\n\n"
|
||||
;
|
||||
|
||||
//
|
||||
// Detailed options
|
||||
//
|
||||
|
||||
device.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
initialization.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
library.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
profiling.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
verification.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
report.print_usage(out);
|
||||
out << "\n";
|
||||
|
||||
about.print_usage(out);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void Options::print_options(std::ostream &out) const {
|
||||
|
||||
out
|
||||
<< "options:\n"
|
||||
<< " help: " << about.help << "\n"
|
||||
<< " mode: " << to_string(execution_mode) << "\n";
|
||||
|
||||
out
|
||||
<< " device:\n";
|
||||
device.print_options(out, 2);
|
||||
|
||||
out
|
||||
<< " initialization:\n";
|
||||
initialization.print_options(out, 2);
|
||||
|
||||
out
|
||||
<< " profiling:\n";
|
||||
profiling.print_options(out, 2);
|
||||
|
||||
out
|
||||
<< " verification:\n";
|
||||
verification.print_options(out, 2);
|
||||
|
||||
out
|
||||
<< " report:\n";
|
||||
report.print_options(out, 2);
|
||||
}
|
||||
|
||||
std::string Options::indent_str(int indent) {
|
||||
return std::string(indent * 2, ' ');
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Command line options for performance test program
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "cutlass/util/command_line.h"
|
||||
#include "cutlass/util/distribution.h"
|
||||
#include "cutlass/library/library.h"
|
||||
|
||||
#include "enumerated_types.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Global options
|
||||
class Options {
|
||||
public:
|
||||
|
||||
/// Cublas and cuDNN options
|
||||
struct Library {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Algorithm mode
|
||||
AlgorithmMode algorithm_mode;
|
||||
|
||||
/// Algorithm enumerants
|
||||
std::vector<int> algorithms;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Library(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
};
|
||||
|
||||
/// Options related to the selected device
|
||||
struct Device {
|
||||
|
||||
/// Device ID
|
||||
int device;
|
||||
|
||||
/// CUDA Device properties
|
||||
cudaDeviceProp properties;
|
||||
|
||||
/// Total memory allocation on device
|
||||
size_t maximum_capacity;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Device(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
void print_device_info(std::ostream &out) const;
|
||||
|
||||
/// Returns the compute capability of the listed device (e.g. 61, 60, 70, 75)
|
||||
int compute_capability() const;
|
||||
};
|
||||
|
||||
/// Options related to initializing input tensors
|
||||
struct Initialization {
|
||||
|
||||
/// If true, data is initialized randomly. If false, no initialization is performed after
|
||||
/// allocating tensors.
|
||||
bool enabled;
|
||||
|
||||
/// Data distribution for input tensors
|
||||
Distribution data_distribution;
|
||||
|
||||
/// Source of random tensor elements
|
||||
Provider provider;
|
||||
|
||||
/// Random number generator seed.
|
||||
int seed;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Initialization(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
|
||||
/// Helper to parse a Distribution object from the command line parser
|
||||
static void get_distribution(
|
||||
cutlass::CommandLine const &args,
|
||||
std::string const &arg,
|
||||
cutlass::Distribution &dist);
|
||||
};
|
||||
|
||||
/// Options related to verification of the result
|
||||
struct Verification {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// If true, kernels are verified before they are profiled
|
||||
bool enabled;
|
||||
|
||||
/// Relative error threshold - zero to require bit-level consistency
|
||||
double epsilon;
|
||||
|
||||
/// Values smaller than this are assumed to be zero
|
||||
double nonzero_floor;
|
||||
|
||||
/// List of providers used to verify each result
|
||||
ProviderVector providers;
|
||||
|
||||
/// Indicates when to save the workspace
|
||||
SaveWorkspace save_workspace;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Verification(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
|
||||
/// Returns true if a provider is enabled
|
||||
bool provider_enabled(Provider provider) const;
|
||||
|
||||
/// Returns the index of a provider if its enabled
|
||||
size_t index(Provider provider) const;
|
||||
};
|
||||
|
||||
/// Options related to profiling
|
||||
struct Profiling {
|
||||
|
||||
/// Number of iterations to warmup each kernel prior to profiling
|
||||
int warmup_iterations;
|
||||
|
||||
/// Number of iterations to profile each kernel - if 0, kernels are launched up to the profiling duration
|
||||
int iterations;
|
||||
|
||||
/// Number of ms to sleep between profiling periods (ms)
|
||||
int sleep_duration;
|
||||
|
||||
/// If true, profiling is actually conducted.
|
||||
bool enabled;
|
||||
|
||||
/// List of providers of each functionality to be profiled
|
||||
ProviderVector providers;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Profiling(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
|
||||
/// Returns true if a provider is enabled
|
||||
bool provider_enabled(Provider provider) const;
|
||||
|
||||
/// Returns the index of a provider if its enabled
|
||||
size_t index(Provider provider) const;
|
||||
};
|
||||
|
||||
/// Options related to reporting
|
||||
struct Report {
|
||||
|
||||
/// If true, result is appended to possibly existing file
|
||||
bool append;
|
||||
|
||||
/// Path to a file containing results
|
||||
std::string output_path;
|
||||
|
||||
/// Sequence of tags to attach to each result
|
||||
std::vector<std::pair<std::string, std::string>> pivot_tags;
|
||||
|
||||
/// If true, reports status of all kernels including those that were
|
||||
/// not run for the given argumetns
|
||||
bool report_not_run;
|
||||
|
||||
/// Prints human-readable text to stdout. If false, nothing is written to stdout
|
||||
bool verbose;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Report(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
};
|
||||
|
||||
/// Options related to printing usage and version information
|
||||
struct About {
|
||||
|
||||
/// If true, usage is printed and the program ends.
|
||||
bool help;
|
||||
|
||||
/// Prints version string
|
||||
bool version;
|
||||
|
||||
/// Print information about devices
|
||||
bool device_info;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
About(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out, int indent = 0) const;
|
||||
|
||||
static void print_version(std::ostream &out);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Top-level execution mode
|
||||
ExecutionMode execution_mode;
|
||||
|
||||
/// Name of math function to profile
|
||||
library::OperationKind operation_kind;
|
||||
|
||||
/// Vector of operation name substrings
|
||||
std::vector<std::string> operation_names;
|
||||
|
||||
//
|
||||
// Detailed configuration options
|
||||
//
|
||||
|
||||
/// Configuration
|
||||
CommandLine cmdline;
|
||||
Device device;
|
||||
Initialization initialization;
|
||||
Library library;
|
||||
Verification verification;
|
||||
Profiling profiling;
|
||||
Report report;
|
||||
About about;
|
||||
|
||||
public:
|
||||
|
||||
Options(CommandLine const &cmdline);
|
||||
|
||||
void print_usage(std::ostream &out) const;
|
||||
void print_options(std::ostream &out) const;
|
||||
|
||||
static std::string indent_str(int indent);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,302 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Execution environment
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
|
||||
#include "performance_report.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#define SHELL_COLOR_BRIGHT() "\033[1;37m"
|
||||
#define SHELL_COLOR_GREEN() "\033[1;32m"
|
||||
#define SHELL_COLOR_RED() "\033[1;31m"
|
||||
#define SHELL_COLOR_END() "\033[0m"
|
||||
|
||||
#else
|
||||
|
||||
#define SHELL_COLOR_BRIGHT() ""
|
||||
#define SHELL_COLOR_GREEN() ""
|
||||
#define SHELL_COLOR_RED() ""
|
||||
#define SHELL_COLOR_END() ""
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PerformanceReport::PerformanceReport(
|
||||
Options const &options,
|
||||
std::vector<std::string> const &argument_names
|
||||
):
|
||||
options_(options), argument_names_(argument_names), problem_index_(0), good_(true) {
|
||||
|
||||
//
|
||||
// Open output file
|
||||
//
|
||||
if (!options_.report.output_path.empty()) {
|
||||
|
||||
bool print_header = true;
|
||||
|
||||
if (options_.report.append) {
|
||||
|
||||
std::ifstream test_output_file(options_.report.output_path.c_str());
|
||||
|
||||
if (test_output_file.is_open()) {
|
||||
print_header = false;
|
||||
test_output_file.close();
|
||||
}
|
||||
|
||||
output_file_.open(options_.report.output_path.c_str(), std::ios::app);
|
||||
}
|
||||
else {
|
||||
output_file_.open(options_.report.output_path.c_str());
|
||||
}
|
||||
|
||||
if (!output_file_.good()) {
|
||||
|
||||
std::cerr << "Could not open output file at path '"
|
||||
<< options_.report.output_path << "'" << std::endl;
|
||||
|
||||
good_ = false;
|
||||
}
|
||||
|
||||
if (print_header) {
|
||||
print_csv_header_(output_file_) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceReport::next_problem() {
|
||||
++problem_index_;
|
||||
}
|
||||
|
||||
void PerformanceReport::append_result(PerformanceResult result) {
|
||||
|
||||
result.problem_index = problem_index_;
|
||||
|
||||
if (options_.report.verbose) {
|
||||
std::cout << "\n";
|
||||
print_result_pretty_(std::cout, result) << std::flush;
|
||||
}
|
||||
|
||||
if (output_file_.is_open()) {
|
||||
print_result_csv_(output_file_, result) << std::endl;
|
||||
}
|
||||
else {
|
||||
concatenated_results_.push_back(result);
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceReport::append_results(PerformanceResultVector const &results) {
|
||||
|
||||
if (options_.report.verbose) {
|
||||
std::cout << "\n\n";
|
||||
}
|
||||
|
||||
// For each result
|
||||
for (auto const & result : results) {
|
||||
append_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceReport::close() {
|
||||
|
||||
//
|
||||
// Output results to stdout if they were not written to a file already.
|
||||
//
|
||||
if (options_.report.verbose && !concatenated_results_.empty()) {
|
||||
|
||||
std::cout << "\n\n";
|
||||
std::cout << "=============================\n\n";
|
||||
std::cout << "CSV Results:\n\n";
|
||||
|
||||
print_csv_header_(std::cout) << std::endl;
|
||||
|
||||
for (auto const &result : concatenated_results_) {
|
||||
print_result_csv_(std::cout, result) << "\n";
|
||||
}
|
||||
}
|
||||
else if (output_file_.is_open() && options_.report.verbose) {
|
||||
std::cout << "\n\nWrote results to '" << options_.report.output_path << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *disposition_status_color(Disposition disposition) {
|
||||
switch (disposition) {
|
||||
case Disposition::kPassed: return SHELL_COLOR_GREEN();
|
||||
case Disposition::kFailed: return SHELL_COLOR_RED();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SHELL_COLOR_END();
|
||||
}
|
||||
|
||||
/// Prints the result in human readable form
|
||||
std::ostream & PerformanceReport::print_result_pretty_(
|
||||
std::ostream &out,
|
||||
PerformanceResult const &result) {
|
||||
|
||||
out << "=============================\n"
|
||||
<< " Problem ID: " << result.problem_index << "\n";
|
||||
|
||||
if (!options_.report.pivot_tags.empty()) {
|
||||
|
||||
out << " Tags: ";
|
||||
|
||||
int column_idx = 0;
|
||||
for (auto const & tag : options_.report.pivot_tags) {
|
||||
out << (column_idx++ ? "," : "") << tag.first << ":" << tag.second;
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
out
|
||||
<< "\n"
|
||||
<< " Provider: " << SHELL_COLOR_BRIGHT() << to_string(result.provider, true) << SHELL_COLOR_END() << "\n"
|
||||
<< " Operation: " << result.operation_name << "\n\n"
|
||||
<< " Disposition: " << disposition_status_color(result.disposition) << to_string(result.disposition, true) << SHELL_COLOR_END() << "\n"
|
||||
<< " Status: " << SHELL_COLOR_BRIGHT() << library::to_string(result.status, true) << SHELL_COLOR_END() << "\n";
|
||||
|
||||
out
|
||||
<< "\n Arguments: ";
|
||||
|
||||
int column_idx = 0;
|
||||
for (auto const &arg : result.arguments) {
|
||||
if (!arg.second.empty()) {
|
||||
out << " --" << arg.first << "=" << arg.second;
|
||||
column_idx += 4 + arg.first.size() + arg.second.size();
|
||||
if (column_idx > 90) {
|
||||
out << " \\\n ";
|
||||
column_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
out << "\n\n";
|
||||
|
||||
out
|
||||
<< " Bytes: " << result.bytes << " bytes\n"
|
||||
<< " FLOPs: " << result.flops << " flops\n\n";
|
||||
|
||||
if (result.good()) {
|
||||
|
||||
out
|
||||
<< " Runtime: " << result.runtime << " ms\n"
|
||||
<< " Memory: " << result.gbytes_per_sec() << " GiB/s\n"
|
||||
<< "\n Math: " << result.gflops_per_sec() << " GFLOP/s\n";
|
||||
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Prints the CSV header
|
||||
std::ostream & PerformanceReport::print_csv_header_(
|
||||
std::ostream &out) {
|
||||
|
||||
int column_idx = 0;
|
||||
|
||||
// Pivot tags
|
||||
for (auto const & tag : options_.report.pivot_tags) {
|
||||
out << (column_idx++ ? "," : "") << tag.first;
|
||||
}
|
||||
|
||||
out
|
||||
<< (column_idx ? "," : "") << "Problem,Provider"
|
||||
<< ",Operation,Disposition,Status";
|
||||
|
||||
for (auto const &arg_name : argument_names_) {
|
||||
out << "," << arg_name;
|
||||
}
|
||||
|
||||
out
|
||||
<< ",Bytes"
|
||||
<< ",Flops"
|
||||
<< ",Runtime"
|
||||
<< ",GB/s"
|
||||
<< ",GFLOPs"
|
||||
;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Print the result in CSV output
|
||||
std::ostream & PerformanceReport::print_result_csv_(
|
||||
std::ostream &out,
|
||||
PerformanceResult const &result) {
|
||||
|
||||
int column_idx = 0;
|
||||
|
||||
// Pivot tags
|
||||
for (auto const & tag : options_.report.pivot_tags) {
|
||||
out << (column_idx++ ? "," : "") << tag.second;
|
||||
}
|
||||
|
||||
out
|
||||
<< (column_idx ? "," : "")
|
||||
<< result.problem_index
|
||||
<< "," << to_string(result.provider, true)
|
||||
<< "," << result.operation_name
|
||||
<< "," << to_string(result.disposition)
|
||||
<< "," << library::to_string(result.status);
|
||||
|
||||
for (auto const & arg : result.arguments) {
|
||||
out << "," << arg.second;
|
||||
}
|
||||
|
||||
out
|
||||
<< "," << result.bytes
|
||||
<< "," << result.flops
|
||||
<< "," << result.runtime;
|
||||
|
||||
if (result.good()) {
|
||||
|
||||
out
|
||||
<< "," << result.gbytes_per_sec()
|
||||
<< "," << result.gflops_per_sec()
|
||||
;
|
||||
}
|
||||
else {
|
||||
out << std::string(2
|
||||
, ','
|
||||
);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,94 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Class performing output during profiling
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#include "options.h"
|
||||
#include "enumerated_types.h"
|
||||
#include "performance_result.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PerformanceReport {
|
||||
private:
|
||||
|
||||
/// Reference to options
|
||||
Options const &options_;
|
||||
|
||||
/// Output file containing results
|
||||
std::ofstream output_file_;
|
||||
|
||||
/// Flag indicating the performance report is valid
|
||||
bool good_;
|
||||
|
||||
/// Vector of argument names
|
||||
std::vector<std::string> argument_names_;
|
||||
|
||||
/// Counter uniquely identifying problem within the report
|
||||
size_t problem_index_;
|
||||
|
||||
/// Collection of all results
|
||||
PerformanceResultVector concatenated_results_;
|
||||
|
||||
public:
|
||||
|
||||
PerformanceReport(Options const &options, std::vector<std::string> const &argument_names);
|
||||
|
||||
bool good() const { return good_; }
|
||||
|
||||
void next_problem();
|
||||
void append_result(PerformanceResult result);
|
||||
void append_results(PerformanceResultVector const &results);
|
||||
|
||||
void close();
|
||||
|
||||
public:
|
||||
|
||||
/// Prints the CSV header
|
||||
std::ostream & print_csv_header_(std::ostream &out);
|
||||
|
||||
/// Prints the CSV
|
||||
std::ostream & print_result_csv_(std::ostream &out, PerformanceResult const &result);
|
||||
|
||||
/// Prints the result in human readable form
|
||||
std::ostream & print_result_pretty_(
|
||||
std::ostream &out,
|
||||
PerformanceResult const &result);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief Defines a math function
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cutlass/cutlass.h"
|
||||
|
||||
#include "enumerated_types.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Performance result object
|
||||
struct PerformanceResult {
|
||||
|
||||
/// Index of problem
|
||||
size_t problem_index;
|
||||
|
||||
/// Provider
|
||||
Provider provider;
|
||||
|
||||
/// Outcome of test
|
||||
Disposition disposition;
|
||||
|
||||
/// CUTLASS status result from kernels
|
||||
Status status;
|
||||
|
||||
/// Operation object
|
||||
std::string operation_name;
|
||||
|
||||
/// Stringified vector of argument values
|
||||
std::vector<std::pair<std::string, std::string> > arguments;
|
||||
|
||||
/// Number of bytes read or written
|
||||
int64_t bytes;
|
||||
|
||||
/// Number of DL flops performed by the math function
|
||||
int64_t flops;
|
||||
|
||||
/// Average runtime in ms
|
||||
double runtime;
|
||||
|
||||
//
|
||||
// Members
|
||||
//
|
||||
|
||||
/// Ctor
|
||||
PerformanceResult():
|
||||
problem_index(0),
|
||||
provider(Provider::kInvalid),
|
||||
disposition(Disposition::kNotRun),
|
||||
status(Status::kInvalid),
|
||||
bytes(0),
|
||||
flops(0),
|
||||
runtime(0)
|
||||
{ }
|
||||
|
||||
/// Returns true if the runtime is valid
|
||||
bool good() const {
|
||||
return runtime > 0;
|
||||
}
|
||||
|
||||
/// Math throughput in units of GFLOP/s
|
||||
double gflops_per_sec() const {
|
||||
return double(flops) / runtime / 1.0e6;
|
||||
}
|
||||
|
||||
/// memory bandwidth in units of GiB/s
|
||||
double gbytes_per_sec() const {
|
||||
return double(bytes) / double(1 << 30) / runtime * 1000.0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using PerformanceResultVector = std::vector<PerformanceResult>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
@@ -0,0 +1,947 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include "problem_space.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
static T lexical_cast(std::string const &str) {
|
||||
std::stringstream ss;
|
||||
T value;
|
||||
|
||||
ss << str;
|
||||
ss >> value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream & KernelArgument::ValueIterator::print(std::ostream &out) const {
|
||||
out << "[" << (void *)this << " " << argument->qualified_name() << "] ";
|
||||
if (this->null_argument) {
|
||||
out << "<null>";
|
||||
}
|
||||
else {
|
||||
out << "<not null>";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
KernelArgument::~KernelArgument() {
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ScalarArgument::ScalarValue::ScalarValue(
|
||||
std::string const &value_,
|
||||
ScalarArgument const *argument_,
|
||||
bool not_null_
|
||||
):
|
||||
KernelArgument::Value(argument_, not_null_),
|
||||
value(value_) {
|
||||
|
||||
}
|
||||
|
||||
std::ostream &ScalarArgument::ScalarValue::print(std::ostream &out) const {
|
||||
out << argument->qualified_name() << ": ";
|
||||
if (not_null) {
|
||||
out << value;
|
||||
}
|
||||
else {
|
||||
out << "<null>";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
ScalarArgument::ScalarValueIterator::ScalarValueIterator(
|
||||
ScalarArgument const *argument_
|
||||
):
|
||||
KernelArgument::ValueIterator(argument_) {
|
||||
|
||||
if (argument_) {
|
||||
value_it = argument_->values.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void ScalarArgument::ScalarValueIterator::operator++() {
|
||||
if (this->null_argument) {
|
||||
this->null_argument = false;
|
||||
}
|
||||
else {
|
||||
++value_it;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScalarArgument::ScalarValueIterator::operator==(ValueIterator const &it) const {
|
||||
if (it.type() != ArgumentTypeID::kScalar) {
|
||||
throw std::runtime_error("Cannot compare ScalarValueIterator with iterator of different type");
|
||||
}
|
||||
auto const & scalar_it = static_cast<ScalarValueIterator const &>(it);
|
||||
return value_it == scalar_it.value_it;
|
||||
}
|
||||
|
||||
/// Gets the value pointed to
|
||||
std::unique_ptr<KernelArgument::Value> ScalarArgument::ScalarValueIterator::at() const {
|
||||
if (this->null_argument) {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new ScalarArgument::ScalarValue(
|
||||
std::string(),
|
||||
static_cast<ScalarArgument const *>(argument),
|
||||
false));
|
||||
}
|
||||
else {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new ScalarArgument::ScalarValue(
|
||||
*value_it,
|
||||
static_cast<ScalarArgument const *>(argument)));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> ScalarArgument::begin() const {
|
||||
return std::unique_ptr<KernelArgument::ValueIterator>(new ScalarValueIterator(this));
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> ScalarArgument::end() const {
|
||||
ScalarValueIterator *it = new ScalarValueIterator(this);
|
||||
it->value_it = this->values.end();
|
||||
it->null_argument = false;
|
||||
return std::unique_ptr<ValueIterator>(it);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IntegerArgument::IntegerValue::IntegerValue(
|
||||
int64_t value_,
|
||||
IntegerArgument const *argument_,
|
||||
bool not_null_
|
||||
): KernelArgument::Value(argument_, not_null_), value(value_) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Pretty printer for debugging
|
||||
std::ostream &IntegerArgument::IntegerValue::print(std::ostream &out) const {
|
||||
out << argument->qualified_name() << ": ";
|
||||
if (not_null) {
|
||||
out << value;
|
||||
}
|
||||
else {
|
||||
out << "<null>";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
IntegerArgument::IntegerValueIterator::IntegerValueIterator(IntegerArgument const *argument_):
|
||||
KernelArgument::ValueIterator(argument_) {
|
||||
|
||||
if (argument_) {
|
||||
range_it = argument_->ranges.begin();
|
||||
if (range_it != argument_->ranges.end()) {
|
||||
value_it = range_it->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegerArgument::IntegerValueIterator::operator++() {
|
||||
|
||||
if (this->null_argument) {
|
||||
this->null_argument = false;
|
||||
}
|
||||
else {
|
||||
++value_it;
|
||||
if (value_it == range_it->end()) {
|
||||
++range_it;
|
||||
if (range_it != static_cast<IntegerArgument const *>(argument)->ranges.end()) {
|
||||
value_it = range_it->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IntegerArgument::IntegerValueIterator::operator==(ValueIterator const &it) const {
|
||||
if (it.type() != ArgumentTypeID::kInteger) {
|
||||
throw std::runtime_error("Cannot compare IntegerValueIterator with iterator of different type");
|
||||
}
|
||||
|
||||
auto const & integer_iterator = static_cast<IntegerValueIterator const &>(it);
|
||||
|
||||
if (this->null_argument) {
|
||||
return it.null_argument;
|
||||
}
|
||||
else {
|
||||
if (range_it != integer_iterator.range_it) {
|
||||
return false;
|
||||
}
|
||||
if (range_it == static_cast<IntegerArgument const *>(argument)->ranges.end() &&
|
||||
range_it == integer_iterator.range_it) {
|
||||
return true;
|
||||
}
|
||||
return value_it == integer_iterator.value_it;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::Value> IntegerArgument::IntegerValueIterator::at() const {
|
||||
if (this->null_argument) {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new IntegerArgument::IntegerValue(
|
||||
0, static_cast<IntegerArgument const *>(argument), false));
|
||||
}
|
||||
else {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new IntegerArgument::IntegerValue(
|
||||
*value_it, static_cast<IntegerArgument const *>(argument)));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> IntegerArgument::begin() const {
|
||||
return std::unique_ptr<KernelArgument::ValueIterator>(new IntegerValueIterator(this));
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> IntegerArgument::end() const {
|
||||
IntegerValueIterator *it = new IntegerValueIterator(this);
|
||||
it->range_it = this->ranges.end();
|
||||
it->null_argument = false;
|
||||
return std::unique_ptr<ValueIterator>(it);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TensorArgument::TensorValue::TensorValue(
|
||||
TensorDescription const &desc_,
|
||||
TensorArgument const *argument_,
|
||||
bool not_null_
|
||||
):
|
||||
KernelArgument::Value(argument_, not_null_),
|
||||
desc(desc_) {
|
||||
|
||||
}
|
||||
|
||||
/// Pretty printer for debugging
|
||||
std::ostream &TensorArgument::TensorValue::print(std::ostream &out) const {
|
||||
out << argument->qualified_name() << ": " << to_string(desc.element) << ": " << to_string(desc.layout);
|
||||
return out;
|
||||
}
|
||||
|
||||
TensorArgument::TensorValueIterator::TensorValueIterator(
|
||||
TensorArgument const *argument_
|
||||
):
|
||||
KernelArgument::ValueIterator(argument_) {
|
||||
|
||||
if (argument_) {
|
||||
value_it = argument_->values.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void TensorArgument::TensorValueIterator::operator++() {
|
||||
if (this->null_argument) {
|
||||
this->null_argument = false;
|
||||
}
|
||||
else {
|
||||
++value_it;
|
||||
}
|
||||
}
|
||||
|
||||
bool TensorArgument::TensorValueIterator::operator==(ValueIterator const &it) const {
|
||||
if (it.type() != ArgumentTypeID::kTensor) {
|
||||
throw std::runtime_error("Cannot compare TensorValueIterator with iterator of different type");
|
||||
}
|
||||
auto const & tensor_it = static_cast<TensorValueIterator const &>(it);
|
||||
return value_it == tensor_it.value_it;
|
||||
}
|
||||
|
||||
/// Gets the value pointed to
|
||||
std::unique_ptr<KernelArgument::Value> TensorArgument::TensorValueIterator::at() const {
|
||||
|
||||
if (this->null_argument) {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new TensorArgument::TensorValue(
|
||||
TensorDescription(), static_cast<TensorArgument const *>(argument), false));
|
||||
}
|
||||
else {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new TensorArgument::TensorValue(
|
||||
*value_it, static_cast<TensorArgument const *>(argument)));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> TensorArgument::begin() const {
|
||||
return std::unique_ptr<KernelArgument::ValueIterator>(new TensorValueIterator(this));
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> TensorArgument::end() const {
|
||||
TensorValueIterator *it = new TensorValueIterator(this);
|
||||
it->value_it = this->values.end();
|
||||
it->null_argument = false;
|
||||
return std::unique_ptr<ValueIterator>(it);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EnumeratedTypeArgument::EnumeratedTypeValue::EnumeratedTypeValue(
|
||||
std::string const & element_,
|
||||
EnumeratedTypeArgument const *argument_,
|
||||
bool not_null_
|
||||
):
|
||||
KernelArgument::Value(argument_, not_null_),
|
||||
element(element_) {
|
||||
|
||||
}
|
||||
|
||||
/// Pretty printer for debugging
|
||||
std::ostream &EnumeratedTypeArgument::EnumeratedTypeValue::print(std::ostream &out) const {
|
||||
out << argument->qualified_name() << ": " << element;
|
||||
return out;
|
||||
}
|
||||
|
||||
EnumeratedTypeArgument::EnumeratedTypeValueIterator::EnumeratedTypeValueIterator(
|
||||
EnumeratedTypeArgument const *argument_
|
||||
):
|
||||
KernelArgument::ValueIterator(argument_) {
|
||||
|
||||
if (argument_) {
|
||||
value_it = argument_->values.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void EnumeratedTypeArgument::EnumeratedTypeValueIterator::operator++() {
|
||||
if (this->null_argument) {
|
||||
this->null_argument = false;
|
||||
}
|
||||
else {
|
||||
++value_it;
|
||||
}
|
||||
}
|
||||
|
||||
bool EnumeratedTypeArgument::EnumeratedTypeValueIterator::operator==(ValueIterator const &it) const {
|
||||
|
||||
if (it.type() != ArgumentTypeID::kEnumerated) {
|
||||
throw std::runtime_error("Cannot compare EnumeratedTypeValueIterator with iterator of different type");
|
||||
}
|
||||
|
||||
auto const & enumerated_type_it = static_cast<EnumeratedTypeValueIterator const &>(it);
|
||||
return value_it == enumerated_type_it.value_it;
|
||||
}
|
||||
|
||||
/// Gets the value pointed to
|
||||
std::unique_ptr<KernelArgument::Value> EnumeratedTypeArgument::EnumeratedTypeValueIterator::at() const {
|
||||
|
||||
if (this->null_argument) {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new EnumeratedTypeValue(
|
||||
std::string(), static_cast<EnumeratedTypeArgument const *>(argument), false));
|
||||
}
|
||||
else {
|
||||
return std::unique_ptr<KernelArgument::Value>(
|
||||
new EnumeratedTypeValue(
|
||||
*value_it, static_cast<EnumeratedTypeArgument const *>(argument)));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> EnumeratedTypeArgument::begin() const {
|
||||
return std::unique_ptr<KernelArgument::ValueIterator>(new EnumeratedTypeValueIterator(this));
|
||||
}
|
||||
|
||||
std::unique_ptr<KernelArgument::ValueIterator> EnumeratedTypeArgument::end() const {
|
||||
EnumeratedTypeValueIterator *it = new EnumeratedTypeValueIterator(this);
|
||||
it->value_it = this->values.end();
|
||||
it->null_argument = false;
|
||||
return std::unique_ptr<ValueIterator>(it);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProblemSpace::Iterator::Iterator() {
|
||||
|
||||
}
|
||||
|
||||
ProblemSpace::Iterator::Iterator(ProblemSpace const &problem_space) {
|
||||
for (auto const & arg_ptr : problem_space.arguments) {
|
||||
construct_(arg_ptr.get());
|
||||
}
|
||||
}
|
||||
|
||||
ProblemSpace::Iterator::Iterator(Iterator && it) {
|
||||
iterators = std::move(it.iterators);
|
||||
}
|
||||
|
||||
/// Helper for recursively constructing iterators
|
||||
void ProblemSpace::Iterator::construct_(KernelArgument const *argument) {
|
||||
iterators.emplace_back(argument->begin());
|
||||
}
|
||||
|
||||
/// Given a set of ranges, iterate over the points within their Cartesian product. No big deal.
|
||||
void ProblemSpace::Iterator::operator++() {
|
||||
|
||||
// Define a pair of iterator into the vector of iterators.
|
||||
IteratorVector::iterator iterator_it = iterators.begin();
|
||||
IteratorVector::iterator next_iterator = iterator_it;
|
||||
|
||||
// Advance the first argument.
|
||||
++(**iterator_it);
|
||||
|
||||
// Maintain a pair of iterators over consecutive arguments.
|
||||
++next_iterator;
|
||||
|
||||
// Carry logic
|
||||
while (next_iterator != iterators.end() &&
|
||||
**iterator_it == *((*iterator_it)->argument->end())) { // Did an iterator reach the end of its range?
|
||||
|
||||
(*iterator_it) = (*iterator_it)->argument->begin(); // Reset that iterator,
|
||||
|
||||
++(**next_iterator); // and increment the next argument's iterator.
|
||||
|
||||
iterator_it = next_iterator; // Advance to the next argument
|
||||
++next_iterator;
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves iterator to end
|
||||
void ProblemSpace::Iterator::move_to_end() {
|
||||
if (!iterators.empty()) {
|
||||
std::unique_ptr<KernelArgument::ValueIterator> new_iter = iterators.back()->argument->end();
|
||||
std::swap(iterators.back(), new_iter);
|
||||
}
|
||||
}
|
||||
|
||||
ProblemSpace::Problem ProblemSpace::Iterator::at() const {
|
||||
Problem problem;
|
||||
|
||||
for (std::unique_ptr<KernelArgument::ValueIterator> const & it : iterators) {
|
||||
problem.emplace_back(it->at());
|
||||
}
|
||||
|
||||
return problem;
|
||||
}
|
||||
|
||||
/// Equality operator
|
||||
bool ProblemSpace::Iterator::operator==(Iterator const &it) const {
|
||||
|
||||
// This would be an opportunity for auto, but explicitly denoting references to
|
||||
// owning smart pointers to dynamic polymorphic objects seems like a kindness to the reader.
|
||||
IteratorVector::const_iterator first_it = iterators.begin();
|
||||
IteratorVector::const_iterator second_it = it.iterators.begin();
|
||||
|
||||
int idx = 0;
|
||||
for (; first_it != iterators.end(); ++first_it, ++second_it, ++idx) {
|
||||
|
||||
KernelArgument::ValueIterator const *my_it = first_it->get();
|
||||
KernelArgument::ValueIterator const *their_it = second_it->get();
|
||||
|
||||
if (*my_it != *their_it) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream &ProblemSpace::Iterator::print(std::ostream &out) const {
|
||||
|
||||
for (std::unique_ptr<KernelArgument::ValueIterator> const & iter_ptr : iterators) {
|
||||
out << " [iter " << (iter_ptr->null_argument ? "null" : "<not null>")
|
||||
<< ", type: " << to_string(iter_ptr->argument->description->type) << "]" << std::endl;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProblemSpace::ProblemSpace(ArgumentDescriptionVector const &schema, CommandLine const &cmdline) {
|
||||
|
||||
// Clone the arguments
|
||||
for (ArgumentDescription const & arg_desc : schema) {
|
||||
clone_(arguments, &arg_desc);
|
||||
}
|
||||
|
||||
// Parse values from the command line
|
||||
for (auto & arg : arguments) {
|
||||
parse_(arg.get(), cmdline);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns the index of an argument by name
|
||||
size_t ProblemSpace::argument_index(char const *name) const {
|
||||
return argument_index_map.at(name);
|
||||
}
|
||||
|
||||
/// Helper for recursively cloning
|
||||
void ProblemSpace::clone_(
|
||||
KernelArgumentVector &kernel_args,
|
||||
ArgumentDescription const *arg_desc) {
|
||||
|
||||
KernelArgument *kernel_arg = nullptr;
|
||||
|
||||
switch (arg_desc->type) {
|
||||
case ArgumentTypeID::kScalar:
|
||||
kernel_arg = new ScalarArgument(arg_desc);
|
||||
break;
|
||||
case ArgumentTypeID::kInteger:
|
||||
kernel_arg = new IntegerArgument(arg_desc);
|
||||
break;
|
||||
case ArgumentTypeID::kTensor:
|
||||
kernel_arg = new TensorArgument(arg_desc);
|
||||
break;
|
||||
case ArgumentTypeID::kStructure:
|
||||
{
|
||||
throw std::runtime_error("ArgumentTypeID::kStructure not supported");
|
||||
}
|
||||
break;
|
||||
case ArgumentTypeID::kEnumerated:
|
||||
kernel_arg = new EnumeratedTypeArgument(arg_desc);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (kernel_arg) {
|
||||
size_t idx = kernel_args.size();
|
||||
for (auto const &alias : arg_desc->aliases) {
|
||||
argument_index_map.insert(std::make_pair(alias, idx));
|
||||
}
|
||||
kernel_args.emplace_back(kernel_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a command line
|
||||
void ProblemSpace::parse_(KernelArgument *arg, CommandLine const &cmdline) {
|
||||
|
||||
switch (arg->description->type) {
|
||||
case ArgumentTypeID::kScalar:
|
||||
{
|
||||
auto * scalar = static_cast<ScalarArgument *>(arg);
|
||||
|
||||
for (auto const &alias : arg->description->aliases) {
|
||||
if (cmdline.check_cmd_line_flag(alias.c_str())) {
|
||||
|
||||
std::vector<std::vector<std::string>> tokens;
|
||||
cmdline.get_cmd_line_argument_ranges(alias.c_str(), tokens);
|
||||
|
||||
for (auto const & vec : tokens) {
|
||||
if (!vec.empty()) {
|
||||
scalar->values.push_back(vec.front());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ArgumentTypeID::kInteger:
|
||||
{
|
||||
auto *integer = static_cast<IntegerArgument *>(arg);
|
||||
|
||||
for (auto const &alias : arg->description->aliases) {
|
||||
if (cmdline.check_cmd_line_flag(alias.c_str())) {
|
||||
|
||||
std::vector<std::vector<std::string> > tokens;
|
||||
cmdline.get_cmd_line_argument_ranges(alias.c_str(), tokens);
|
||||
|
||||
for (auto const &range_tokens : tokens) {
|
||||
|
||||
if (!range_tokens.empty()) {
|
||||
Range range(lexical_cast<int64_t>(range_tokens.front()));
|
||||
|
||||
if (range_tokens.size() > 1) {
|
||||
range.last = lexical_cast<int64_t>(range_tokens.at(1));
|
||||
}
|
||||
|
||||
if (range_tokens.size() > 2) {
|
||||
range.increment = lexical_cast<int64_t>(range_tokens.at(2));
|
||||
}
|
||||
|
||||
integer->ranges.push_back(range);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ArgumentTypeID::kTensor:
|
||||
{
|
||||
auto *tensor = static_cast<TensorArgument *>(arg);
|
||||
|
||||
for (auto const &alias : arg->description->aliases) {
|
||||
if (cmdline.check_cmd_line_flag(alias.c_str())) {
|
||||
|
||||
std::vector<std::vector<std::string>> tokens;
|
||||
|
||||
cmdline.get_cmd_line_argument_ranges(alias.c_str(), tokens);
|
||||
|
||||
for (auto const & tensor_tokens : tokens) {
|
||||
if (!tensor_tokens.empty()) {
|
||||
TensorArgument::TensorDescription tensor_desc;
|
||||
|
||||
tensor_desc.element = cutlass::library::from_string<library::NumericTypeID>(tensor_tokens.front());
|
||||
|
||||
// Layout
|
||||
if (tensor_tokens.size() > 1) {
|
||||
tensor_desc.layout = cutlass::library::from_string<library::LayoutTypeID>(tensor_tokens.at(1));
|
||||
}
|
||||
|
||||
// Stride
|
||||
for (size_t i = 2; i < tensor_tokens.size(); ++i) {
|
||||
tensor_desc.stride.push_back(lexical_cast<int>(tensor_tokens.at(i)));
|
||||
}
|
||||
|
||||
tensor->values.push_back(tensor_desc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ArgumentTypeID::kStructure:
|
||||
{
|
||||
throw std::runtime_error("Structure arguments not supported");
|
||||
}
|
||||
break;
|
||||
case ArgumentTypeID::kEnumerated:
|
||||
{
|
||||
auto *enumerated_type = static_cast<EnumeratedTypeArgument *>(arg);
|
||||
|
||||
for (auto const &alias : arg->description->aliases) {
|
||||
if (cmdline.check_cmd_line_flag(alias.c_str())) {
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
cmdline.get_cmd_line_arguments(alias.c_str(), tokens);
|
||||
|
||||
for (auto const & token : tokens) {
|
||||
enumerated_type->values.push_back(token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProblemSpace::Iterator ProblemSpace::begin() const {
|
||||
return ProblemSpace::Iterator(*this);
|
||||
}
|
||||
|
||||
ProblemSpace::Iterator ProblemSpace::end() const {
|
||||
ProblemSpace::Iterator it(*this);
|
||||
it.move_to_end();
|
||||
return it;
|
||||
}
|
||||
|
||||
/// Gets all argument names as an ordered vector
|
||||
std::vector<std::string> ProblemSpace::argument_names() const {
|
||||
|
||||
Problem problem = this->begin().at();
|
||||
|
||||
std::vector<std::string> names;
|
||||
names.reserve(problem.size());
|
||||
|
||||
for (auto const & arg : problem) {
|
||||
names.push_back(arg->argument->description->aliases.front());
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_int(int64_t &int_value, KernelArgument::Value const *value_ptr) {
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kInteger) {
|
||||
int_value = static_cast<IntegerArgument::IntegerValue const *>(value_ptr)->value;
|
||||
}
|
||||
else if (value_ptr->argument->description->type == ArgumentTypeID::kScalar) {
|
||||
std::stringstream ss;
|
||||
ss << static_cast<ScalarArgument::ScalarValue const *>(value_ptr)->value;
|
||||
ss >> int_value;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
"arg_as_int64_t() - illegal cast. Problem space argument must be integer or scalar");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Lexically casts an argument to an int64
|
||||
bool arg_as_int(
|
||||
int64_t &int_value,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
return arg_as_int(int_value, value_ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_NumericTypeID(
|
||||
library::NumericTypeID &numeric_type,
|
||||
KernelArgument::Value const *value_ptr) {
|
||||
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kEnumerated) {
|
||||
|
||||
numeric_type = library::from_string<library::NumericTypeID>(
|
||||
static_cast<EnumeratedTypeArgument::EnumeratedTypeValue const *>(value_ptr)->element);
|
||||
|
||||
if (numeric_type == library::NumericTypeID::kInvalid) {
|
||||
throw std::runtime_error(
|
||||
"arg_as_NumericTypeID() - illegal cast.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
throw std::runtime_error(
|
||||
"arg_as_NumericTypeID() - illegal cast.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_NumericTypeID(
|
||||
library::NumericTypeID &numeric_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
return arg_as_NumericTypeID(numeric_type, value_ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_LayoutTypeID(
|
||||
library::LayoutTypeID &layout_type,
|
||||
KernelArgument::Value const *value_ptr) {
|
||||
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kEnumerated) {
|
||||
|
||||
layout_type = library::from_string<library::LayoutTypeID>(
|
||||
static_cast<EnumeratedTypeArgument::EnumeratedTypeValue const *>(value_ptr)->element);
|
||||
|
||||
if (layout_type == library::LayoutTypeID::kInvalid) {
|
||||
throw std::runtime_error(
|
||||
"arg_as_LayoutTypeID() - illegal cast.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
throw std::runtime_error(
|
||||
"arg_as_LayoutTypeID() - illegal cast.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_LayoutTypeID(
|
||||
library::LayoutTypeID &layout_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
return arg_as_LayoutTypeID(layout_type, value_ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_OpcodeClassID(
|
||||
library::OpcodeClassID &opcode_class,
|
||||
KernelArgument::Value const *value_ptr) {
|
||||
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kEnumerated) {
|
||||
|
||||
opcode_class = library::from_string<library::OpcodeClassID>(
|
||||
static_cast<EnumeratedTypeArgument::EnumeratedTypeValue const *>(value_ptr)->element);
|
||||
|
||||
if (opcode_class == library::OpcodeClassID::kInvalid) {
|
||||
throw std::runtime_error(
|
||||
"arg_as_OpcodeClassID() - illegal cast.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
throw std::runtime_error(
|
||||
"arg_as_OpcodeClassID() - illegal cast.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_OpcodeClassID(
|
||||
library::OpcodeClassID &opcode_class,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
return arg_as_OpcodeClassID(opcode_class, value_ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to a given type stored in a byte array. Returns true if not null.
|
||||
bool arg_as_scalar(
|
||||
std::vector<uint8_t> &bytes,
|
||||
library::NumericTypeID numeric_type,
|
||||
KernelArgument::Value const *value_ptr) {
|
||||
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kInteger) {
|
||||
int64_t int_value = static_cast<IntegerArgument::IntegerValue const *>(value_ptr)->value;
|
||||
|
||||
// TODO - convert int64_t => destination type
|
||||
}
|
||||
else if (value_ptr->argument->description->type == ArgumentTypeID::kScalar) {
|
||||
std::string const &str_value = static_cast<ScalarArgument::ScalarValue const *>(value_ptr)->value;
|
||||
|
||||
return lexical_cast(bytes, numeric_type, str_value);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
"arg_as_int() - illegal cast. Problem space argument must be integer or scalar");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Lexically casts an argument to a given type and returns a byte array
|
||||
bool arg_as_scalar(
|
||||
std::vector<uint8_t> &bytes,
|
||||
library::NumericTypeID numeric_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
return arg_as_scalar(bytes, numeric_type, value_ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns true if a tensor description satisfies a `tensor` value
|
||||
bool tensor_description_satisfies(
|
||||
library::TensorDescription const &tensor_desc,
|
||||
TensorArgument::TensorValue const *value_ptr) {
|
||||
|
||||
if (value_ptr->not_null) {
|
||||
if (value_ptr->desc.element != library::NumericTypeID::kUnknown &&
|
||||
value_ptr->desc.element != tensor_desc.element) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value_ptr->desc.layout != library::LayoutTypeID::kUnknown &&
|
||||
value_ptr->desc.layout != tensor_desc.layout) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns true if a tensor description satisfies a `tensor` value
|
||||
bool tensor_description_satisfies(
|
||||
library::TensorDescription const &tensor_desc,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem) {
|
||||
|
||||
size_t idx = problem_space.argument_index(name);
|
||||
KernelArgument::Value const *value_ptr = problem.at(idx).get();
|
||||
|
||||
if (value_ptr->argument->description->type == ArgumentTypeID::kTensor) {
|
||||
return tensor_description_satisfies(
|
||||
tensor_desc,
|
||||
static_cast<TensorArgument::TensorValue const *>(value_ptr));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Kernel argument mismatch");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,846 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
/* \file
|
||||
\brief
|
||||
|
||||
"Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified,
|
||||
bug-ridden, slow implementation of half of Common Lisp."
|
||||
|
||||
- Greenspun's Tenth Rule of Programming
|
||||
|
||||
|
||||
cutlass::profiler::ProblemSpace defines a set of data structures which represent the Cartesian
|
||||
product of sequences defined by integer ranges, lists of scalars, and sets of enumerated types.
|
||||
|
||||
These permit a single invocation of the CUTLASS Profiler to iterate over a large set of problems,
|
||||
verify and profile various operations when they are compatible with the command line, and
|
||||
construct data tables of results that are convenient inputs to post processing in Excel or Pandas.
|
||||
|
||||
By executing multiple problems per invocation, startup overheads may be amortized across many
|
||||
kernel launches.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard Library includes
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
// CUTLASS Utility includes
|
||||
#include "cutlass/util/command_line.h"
|
||||
|
||||
// CUTLASS Library includes
|
||||
#include "cutlass/library/library.h"
|
||||
|
||||
// Profiler includes
|
||||
#include "enumerated_types.h"
|
||||
|
||||
namespace cutlass {
|
||||
namespace profiler {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Defines the argument schema
|
||||
struct ArgumentDescription {
|
||||
|
||||
/// Type of argument
|
||||
ArgumentTypeID type;
|
||||
|
||||
/// Prioritized array of aliases used in command line parsing
|
||||
std::vector<std::string> aliases;
|
||||
|
||||
/// Description of argument
|
||||
std::string description;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
ArgumentDescription():
|
||||
type(ArgumentTypeID::kInvalid) { }
|
||||
|
||||
/// Constructor with aliases
|
||||
ArgumentDescription(
|
||||
ArgumentTypeID type_,
|
||||
std::vector<std::string> const &aliases_,
|
||||
std::string const &description_
|
||||
):
|
||||
type(type_), aliases(aliases_), description(description_) { }
|
||||
};
|
||||
|
||||
/// Vector of arguments
|
||||
using ArgumentDescriptionVector = std::vector<ArgumentDescription>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Base class for kernel arguments
|
||||
struct KernelArgument {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
/// Value base class
|
||||
struct Value {
|
||||
|
||||
KernelArgument const *argument;
|
||||
bool not_null;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Value(
|
||||
KernelArgument const *argument_ = nullptr,
|
||||
bool not_null_ = true
|
||||
): argument(argument_), not_null(not_null_) { }
|
||||
|
||||
virtual ~Value() { }
|
||||
|
||||
virtual std::ostream &print(std::ostream &out) const =0;
|
||||
};
|
||||
|
||||
/// Abstract base class to iterate over values within arguments
|
||||
struct ValueIterator {
|
||||
|
||||
/// Indicates type of kernel argument
|
||||
KernelArgument const *argument;
|
||||
|
||||
/// If the iterator points to an argument that is null, it needs to be distinguished
|
||||
/// from end.
|
||||
bool null_argument;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Constructs a value iterator - no methods are valid if argument_ == nullptr
|
||||
ValueIterator(
|
||||
KernelArgument const *argument_ = nullptr,
|
||||
bool null_argument_ = false):
|
||||
argument(argument_), null_argument(null_argument_) {
|
||||
|
||||
if (!argument_->not_null()) {
|
||||
null_argument = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~ValueIterator() { }
|
||||
|
||||
/// Advances to next point in range
|
||||
virtual void operator++() = 0;
|
||||
|
||||
/// Compares against another value iterator - must be of the same KernelArgument type
|
||||
virtual bool operator==(ValueIterator const &it) const = 0;
|
||||
|
||||
/// Returns a unique_ptr<Value> object pointing to a newly created value object
|
||||
virtual std::unique_ptr<Value> at() const = 0;
|
||||
|
||||
/// Gets the type of the iterator
|
||||
ArgumentTypeID type() const {
|
||||
return argument->description->type;
|
||||
}
|
||||
|
||||
/// Helper to compute inequality
|
||||
bool operator!=(ValueIterator const &it) const {
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
std::ostream &print(std::ostream &out) const;
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Describes the argument
|
||||
ArgumentDescription const *description;
|
||||
|
||||
/// Parent node
|
||||
KernelArgument *parent;
|
||||
|
||||
/// Sequence in which the kernel argument is to be iterated over.
|
||||
/// Smaller means faster changing. -1 is don't care
|
||||
int ordinal;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
KernelArgument(
|
||||
ArgumentDescription const *description_ = nullptr,
|
||||
KernelArgument *parent_ = nullptr,
|
||||
int ordinal_ = -1
|
||||
): description(description_), parent(parent_), ordinal(ordinal_) { }
|
||||
|
||||
virtual ~KernelArgument();
|
||||
|
||||
/// Returns true if the kernel argument iself is empty
|
||||
virtual bool not_null() const =0;
|
||||
|
||||
/// Returns a string name for debugging
|
||||
std::string qualified_name() const {
|
||||
if (description) {
|
||||
if (description->aliases.empty()) {
|
||||
return "<description_not_null_no_aliases>";
|
||||
}
|
||||
return description->aliases.front();
|
||||
}
|
||||
return "<description_null>";
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<ValueIterator> begin() const =0;
|
||||
virtual std::unique_ptr<ValueIterator> end() const =0;
|
||||
};
|
||||
|
||||
using KernelArgumentVector = std::vector<std::unique_ptr<KernelArgument>>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Defines a scalar argument type as a string that is lexically cast to the appropriate kernel
|
||||
/// type.
|
||||
struct ScalarArgument : public KernelArgument {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
/// Value type
|
||||
struct ScalarValue : public KernelArgument::Value {
|
||||
|
||||
std::string value;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
ScalarValue(
|
||||
std::string const &value_ = "",
|
||||
ScalarArgument const *argument = nullptr,
|
||||
bool not_null_ = true
|
||||
);
|
||||
|
||||
virtual std::ostream &print(std::ostream &out) const;
|
||||
};
|
||||
|
||||
using ValueCollection = std::vector<std::string>;
|
||||
|
||||
/// Abstract base class to iterate over values within arguments
|
||||
struct ScalarValueIterator : public KernelArgument::ValueIterator {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
ValueCollection::const_iterator value_it;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
ScalarValueIterator(ScalarArgument const *argument = nullptr);
|
||||
|
||||
virtual void operator++();
|
||||
virtual bool operator==(ValueIterator const &it) const;
|
||||
|
||||
/// Gets the value pointed to
|
||||
virtual std::unique_ptr<KernelArgument::Value> at() const;
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Set of posible values
|
||||
ValueCollection values;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
ScalarArgument(
|
||||
ArgumentDescription const *description
|
||||
):
|
||||
KernelArgument(description) { }
|
||||
|
||||
virtual bool not_null() const {
|
||||
return !values.empty();
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> begin() const;
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> end() const;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Closed range supporting additive increment
|
||||
struct Range {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
struct Iterator {
|
||||
|
||||
int64_t value;
|
||||
int64_t increment;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
Iterator(
|
||||
int64_t value_ = 0,
|
||||
int64_t increment_ = 1
|
||||
):
|
||||
value(value_), increment(increment_) { }
|
||||
|
||||
Iterator & operator++() {
|
||||
value += increment;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
Iterator self(*this);
|
||||
++(*this);
|
||||
return self;
|
||||
}
|
||||
|
||||
bool operator==(Iterator const &it) const {
|
||||
return value == it.value;
|
||||
}
|
||||
|
||||
bool operator!=(Iterator const &it) const {
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
int64_t at() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
int64_t operator*() const {
|
||||
return at();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
int64_t first; ///< first element in range
|
||||
int64_t last; ///< last element in range
|
||||
int64_t increment; ///< additive increment between values
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default constructor - range acts as a scalar
|
||||
Range(int64_t first_ = 0): first(first_), last(first_), increment(1) { }
|
||||
|
||||
/// Range acts as a range
|
||||
Range(
|
||||
int64_t first_,
|
||||
int64_t last_,
|
||||
int64_t increment_ = 1
|
||||
): first(first_), last(last_), increment(increment_) {
|
||||
|
||||
// Helpers to avoid constructing invalid ranges
|
||||
if (increment > 0) {
|
||||
if (last < first) {
|
||||
std::swap(last, first);
|
||||
}
|
||||
}
|
||||
else if (increment < 0) {
|
||||
if (first < last) {
|
||||
std::swap(last, first);
|
||||
}
|
||||
}
|
||||
else if (last != first) {
|
||||
last = first;
|
||||
increment = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element within the range
|
||||
Iterator begin() const {
|
||||
return Iterator(first, increment);
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element *after* the range
|
||||
Iterator end() const {
|
||||
return Iterator(first + ((last - first)/increment + 1) * increment, increment);
|
||||
}
|
||||
};
|
||||
|
||||
/// Integer-valued argument - represented as a list of integer-valued ranges
|
||||
struct IntegerArgument : public KernelArgument {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
/// Value type
|
||||
struct IntegerValue : public KernelArgument::Value {
|
||||
|
||||
int64_t value;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
IntegerValue(
|
||||
int64_t value_ = 0,
|
||||
IntegerArgument const *argument_ = nullptr,
|
||||
bool not_null_ = true
|
||||
);
|
||||
|
||||
/// Pretty printer for debugging
|
||||
virtual std::ostream &print(std::ostream &out) const;
|
||||
};
|
||||
|
||||
/// Collection of ranges represent the IntegerArgument's state
|
||||
using RangeCollection = std::vector<Range>;
|
||||
|
||||
/// Abstract base class to iterate over values within arguments
|
||||
struct IntegerValueIterator : public KernelArgument::ValueIterator {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
RangeCollection::const_iterator range_it;
|
||||
Range::Iterator value_it;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
IntegerValueIterator();
|
||||
IntegerValueIterator(IntegerArgument const *argument);
|
||||
|
||||
virtual void operator++();
|
||||
virtual bool operator==(ValueIterator const &it) const;
|
||||
|
||||
/// Gets the value pointed to
|
||||
virtual std::unique_ptr<KernelArgument::Value> at() const;
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
/// Set of posible values
|
||||
RangeCollection ranges;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
IntegerArgument(
|
||||
ArgumentDescription const *description
|
||||
):
|
||||
KernelArgument(description) { }
|
||||
|
||||
virtual bool not_null() const {
|
||||
bool _not_null = !ranges.empty();
|
||||
return _not_null;
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> begin() const;
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> end() const;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Structure defining the data type of tensors
|
||||
struct TensorArgument : public KernelArgument {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
struct TensorDescription {
|
||||
|
||||
/// Data type of elements
|
||||
library::NumericTypeID element;
|
||||
|
||||
/// Layout definition
|
||||
library::LayoutTypeID layout;
|
||||
|
||||
/// Computed extent
|
||||
std::vector<int> extent;
|
||||
|
||||
/// Enables directly specifying stride value used to size tensor
|
||||
std::vector<int> stride;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorDescription(
|
||||
library::NumericTypeID element_ = library::NumericTypeID::kUnknown,
|
||||
library::LayoutTypeID layout_ = library::LayoutTypeID::kUnknown,
|
||||
std::vector<int> extent_ = std::vector<int>(),
|
||||
std::vector<int> stride_ = std::vector<int>()
|
||||
):
|
||||
element(element_), layout(layout_), extent(extent_), stride(stride_) {}
|
||||
};
|
||||
|
||||
using ValueCollection = std::vector<TensorDescription>;
|
||||
|
||||
/// Value structure
|
||||
struct TensorValue : public KernelArgument::Value {
|
||||
|
||||
TensorDescription desc;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorValue(
|
||||
TensorDescription const &desc_ = TensorDescription(),
|
||||
TensorArgument const *argument_ = nullptr,
|
||||
bool not_null_ = true
|
||||
);
|
||||
|
||||
/// Pretty printer for debugging
|
||||
virtual std::ostream &print(std::ostream &out) const;
|
||||
};
|
||||
|
||||
/// Abstract base class to iterate over values within arguments
|
||||
struct TensorValueIterator : public KernelArgument::ValueIterator {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
ValueCollection::const_iterator value_it;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
TensorValueIterator(TensorArgument const *argument_);
|
||||
|
||||
virtual void operator++();
|
||||
virtual bool operator==(ValueIterator const &it) const;
|
||||
|
||||
/// Gets the value pointed to
|
||||
virtual std::unique_ptr<KernelArgument::Value> at() const;
|
||||
};
|
||||
|
||||
/// Set of possible values
|
||||
ValueCollection values;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
TensorArgument(
|
||||
ArgumentDescription const *description
|
||||
):
|
||||
KernelArgument(description) { }
|
||||
|
||||
virtual bool not_null() const {
|
||||
return !values.empty();
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> begin() const;
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> end() const;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Numeric data type
|
||||
struct EnumeratedTypeArgument : public KernelArgument {
|
||||
|
||||
//
|
||||
// Type definitions
|
||||
//
|
||||
|
||||
struct EnumeratedTypeValue : public KernelArgument::Value {
|
||||
|
||||
/// Data type of element
|
||||
std::string element;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
EnumeratedTypeValue(
|
||||
std::string const &element_ = std::string(),
|
||||
EnumeratedTypeArgument const *argument_ = nullptr,
|
||||
bool not_null_ = true
|
||||
);
|
||||
|
||||
/// Pretty printer for debugging
|
||||
virtual std::ostream &print(std::ostream &out) const;
|
||||
};
|
||||
|
||||
using ValueCollection = std::vector<std::string>;
|
||||
|
||||
/// Abstract base class to iterate over values within arguments
|
||||
struct EnumeratedTypeValueIterator : public KernelArgument::ValueIterator {
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
ValueCollection::const_iterator value_it;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
EnumeratedTypeValueIterator(EnumeratedTypeArgument const *argument_ = nullptr);
|
||||
|
||||
virtual void operator++();
|
||||
virtual bool operator==(ValueIterator const &it) const;
|
||||
|
||||
/// Gets the value pointed to
|
||||
virtual std::unique_ptr<KernelArgument::Value> at() const;
|
||||
};
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
ValueCollection values;
|
||||
|
||||
//
|
||||
// Members
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
EnumeratedTypeArgument(ArgumentDescription const *description):
|
||||
KernelArgument(description) {}
|
||||
|
||||
virtual bool not_null() const {
|
||||
return !values.empty();
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> begin() const;
|
||||
virtual std::unique_ptr<KernelArgument::ValueIterator> end() const;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Object storing the space argument values
|
||||
class ProblemSpace {
|
||||
public:
|
||||
|
||||
/// Tuple of arguments
|
||||
using Problem = std::vector<std::unique_ptr<KernelArgument::Value>>;
|
||||
|
||||
/// Type used to iterator over things
|
||||
using IteratorVector = std::vector<std::unique_ptr<KernelArgument::ValueIterator>>;
|
||||
|
||||
/// Iterates over points in the design space
|
||||
class Iterator {
|
||||
private:
|
||||
|
||||
/// One iterator per argument
|
||||
IteratorVector iterators;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
explicit Iterator();
|
||||
Iterator(ProblemSpace const &problem_space);
|
||||
Iterator(Iterator &&it);
|
||||
|
||||
// Rule of three
|
||||
Iterator(Iterator const &) = delete;
|
||||
Iterator &operator=(Iterator const &it) = delete;
|
||||
~Iterator() = default;
|
||||
|
||||
/// Pre-increment - advances to next point in argument range
|
||||
void operator++();
|
||||
|
||||
/// Gets the current argument value
|
||||
Problem at() const;
|
||||
|
||||
/// Moves iterator to end
|
||||
void move_to_end();
|
||||
|
||||
/// Equality operator
|
||||
bool operator==(Iterator const &it) const;
|
||||
|
||||
/// Inequality operator
|
||||
bool operator!=(Iterator const &it) const {
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
/// Helper to call at() method
|
||||
Problem operator*() const {
|
||||
return at();
|
||||
}
|
||||
|
||||
/// Helper to print iterator state
|
||||
std::ostream & print(std::ostream &out) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Helper for recursively constructing iterators
|
||||
void construct_(KernelArgument const *argument);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Data members
|
||||
//
|
||||
|
||||
KernelArgumentVector arguments;
|
||||
|
||||
/// Map of argument names to their position within the argument vector
|
||||
std::unordered_map<std::string, size_t> argument_index_map;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/// Default ctor
|
||||
ProblemSpace() {}
|
||||
|
||||
/// Constructs a problem space from a vector of arguments. This vector must outlive
|
||||
/// the ProblemSpace object, which stores pointers to objects within the
|
||||
/// ArgumentDescriptionVector.
|
||||
ProblemSpace(ArgumentDescriptionVector const &schema, CommandLine const &cmdline);
|
||||
|
||||
Iterator begin() const; // returns an iterator to the first point in the range
|
||||
Iterator end() const; // returns an iterator to the first point after the range
|
||||
|
||||
/// Returns the index of an argument by name
|
||||
size_t argument_index(char const *name) const;
|
||||
|
||||
/// Gets all argument names as an ordered vector
|
||||
std::vector<std::string> argument_names() const;
|
||||
|
||||
/// Returns the number of dimensions of the problem space
|
||||
size_t rank() const { return arguments.size(); }
|
||||
|
||||
private:
|
||||
|
||||
/// Helper for recursively cloning
|
||||
void clone_(
|
||||
KernelArgumentVector &kernel_args,
|
||||
ArgumentDescription const *arg_desc);
|
||||
|
||||
/// Parses command line argument
|
||||
void parse_(
|
||||
KernelArgument *arg,
|
||||
CommandLine const &cmdline);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_int(int64_t &int_value, KernelArgument::Value const *value_ptr);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_int(
|
||||
int64_t &int_value,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_NumericTypeID(library::NumericTypeID &numeric_type, KernelArgument::Value const *value_ptr);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_NumericTypeID(
|
||||
library::NumericTypeID &numeric_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_LayoutTypeID(library::LayoutTypeID &layout_type, KernelArgument::Value const *value_ptr);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_LayoutTypeID(
|
||||
library::LayoutTypeID &layout_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_OpcodeClassID(library::OpcodeClassID &opcode_class, KernelArgument::Value const *value_ptr);
|
||||
|
||||
/// Lexically casts an argument to an int64 if it is defined. Returns true if not null.
|
||||
bool arg_as_OpcodeClassID(
|
||||
library::OpcodeClassID &opcode_class,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Lexically casts an argument to a given type stored in a byte array. Returns true if not null.
|
||||
bool arg_as_scalar(
|
||||
std::vector<uint8_t> &bytes,
|
||||
library::NumericTypeID numeric_type,
|
||||
KernelArgument::Value const *value_ptr);
|
||||
|
||||
/// Lexically casts an argument to a given type stored in a byte array. Returns true if not null.
|
||||
bool arg_as_scalar(
|
||||
std::vector<uint8_t> &bytes,
|
||||
library::NumericTypeID numeric_type,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/// Returns true if a tensor description satisfies a `tensor` value
|
||||
bool tensor_description_satisfies(
|
||||
library::TensorDescription const &tensor_desc,
|
||||
TensorArgument::TensorValue const *value_ptr);
|
||||
|
||||
/// Returns true if a tensor description satisfies a `tensor` value
|
||||
bool tensor_description_satisfies(
|
||||
library::TensorDescription const &tensor_desc,
|
||||
char const *name,
|
||||
ProblemSpace const &problem_space,
|
||||
ProblemSpace::Problem const &problem);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace cutlass
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user