From 8b4c7303653bd79865e5668d0dc7a10788a125e8 Mon Sep 17 00:00:00 2001 From: SiCong Li Date: Thu, 19 Sep 2019 12:18:15 +0100 Subject: COMPMID-2566 - Add CLGEMMReshapedOnlyRHS example * Add example * Add shell script for running example on predefined tunable configs Change-Id: I1660c37a071492b4da5cf97c1ced5ac4a08b19ce Signed-off-by: SiCong Li Reviewed-on: https://review.mlplatform.org/c/1961 Comments-Addressed: Arm Jenkins Reviewed-by: Gian Marco Iodice Tested-by: Arm Jenkins --- examples/SConscript | 10 + examples/gemm_tuner/README.md | 11 + examples/gemm_tuner/benchmark_gemm_examples.sh | 296 ++++++++++++++++++++++ examples/gemm_tuner/cl_gemm_reshaped_rhs_only.cpp | 225 ++++++++++++++++ 4 files changed, 542 insertions(+) create mode 100644 examples/gemm_tuner/README.md create mode 100755 examples/gemm_tuner/benchmark_gemm_examples.sh create mode 100644 examples/gemm_tuner/cl_gemm_reshaped_rhs_only.cpp (limited to 'examples') diff --git a/examples/SConscript b/examples/SConscript index d08aa9da7c..d283b711bf 100644 --- a/examples/SConscript +++ b/examples/SConscript @@ -86,6 +86,16 @@ if env['opencl']: alias = examples_env.Alias(example, prog) Default(alias) +if env['gemm_tuner'] and env['opencl']: + for file in Glob("./gemm_tuner/cl_*.cpp"): + example = os.path.basename(os.path.splitext(str(file))[0]) + example = os.path.join("gemm_tuner", example) + prog = examples_env.Program(example, ["{}.cpp".format(example), utils], CPPDEFINES=['ARM_COMPUTE_CL'], LIBS = examples_libs + arm_compute_libs) + Depends(prog, arm_compute_dependency) + prog = install_bin(prog) + alias = examples_env.Alias(example, prog) + Default(alias) + if env['neon']: for file in Glob("./neon_*.cpp"): example = os.path.basename(os.path.splitext(str(file))[0]) diff --git a/examples/gemm_tuner/README.md b/examples/gemm_tuner/README.md new file mode 100644 index 0000000000..789dc2bb9a --- /dev/null +++ b/examples/gemm_tuner/README.md @@ -0,0 +1,11 @@ +# Gemm Tuner + +## Pre-requisite +(Preferably) bash shell +benchmark examples + +## Usage +Run gemm examples of a selected strategy, over all pre-defined tunable configurations, on a set of gemm shapes provided +by the user. Save the benchmark results to json files in an output directory. + +[$SHELL] ./benchmark_gemm_examples.sh -e \ -s \ -g \ -c \ [-o \, [-i \]] diff --git a/examples/gemm_tuner/benchmark_gemm_examples.sh b/examples/gemm_tuner/benchmark_gemm_examples.sh new file mode 100755 index 0000000000..e3ed1aefde --- /dev/null +++ b/examples/gemm_tuner/benchmark_gemm_examples.sh @@ -0,0 +1,296 @@ +# Copyright (c) 2019 ARM Limited. +# +# SPDX-License-Identifier: MIT +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +#!/bin/sh + +# Global: Global variables and global settings {{{ +# Treat unset variables as an error when substituting +set -u + +CMD=$( basename $0 ) + +# All supported strategy options +ALL_STRATEGY_OPTIONS="reshaped_rhs_only" + +# Default strategy option +DEFAULT_STRATEGY_OPTION="reshaped_rhs_only" + +# Names of example binary for each strategy +EXAMPLE_BIN_RESHAPED_RHS_ONLY="benchmark_cl_gemm_reshaped_rhs_only" + +# Default output directory +DEFAULT_OUT_DIR="out" + +# Number of iterations for each benchmark run +NUM_ITERATION=3 +# Global }}} + +# Functions {{{ +####################################### +# Print gemm shape file help message +# Globals: +# None +# Arguments: +# None +# Returns: +# None +####################################### +function help_gemm_shape_file() { + cat >&2 << EOF +Gemm shape file: + Gemm shape file is a headerless csv file with fields separated by commas and commas only (there cannot be whitespaces + around each field). + A gemm shape is a list of 4 positive integers describing the shapes of the two matrices (LHS and RHS) + with: + M - Number of lhs matrix rows + N - Number of rhs matrix columns + K - Number of lhs matrix columns/rhs matrix rows + B - Batch size + + An example gemm shape file looks like: + 100,100,30,1 + 100,100,30,3 + ... + +EOF +} + +####################################### +# Print gemm config file for reshaped_rhs_only +# Globals: +# None +# Arguments: +# None +# Returns: +# None +####################################### +function help_gemm_config_file_reshaped_rhs_only() { + cat >&2 << EOF +Gemm config file (Strategy reshaped_rhs_only): + Gemm config file is a headerless csv file with fields separated by commas and commas only (there cannot be whitespaces + around each field). + A gemm config is a list of 4 positive integers and 2 boolean values interleave_rhs and transpose_rhs, with: + m0 - Number of rows processed by the matrix multiplication + n0 - Number of columns processed by the matrix multiplication + k0 - Number of partial accumulations performed by the matrix multiplication + h0 - Number of horizontal blocks of size (k0xn0) stored on the same output row + interleave_rhs - Interleave rhs matrix (1) / Do not interleave rhs matrix (0) + transpose_rhs - Interleave rhs matrix (1) / Do not transpose rhs matrix (0) + + Only the following configurations of M0, N0 and K0 are currently supported: + M0 = 1, 2, 3, 4, 5, 6, 7, 8 + N0 = 2, 3, 4, 8, 16 + K0 = 2, 3, 4, 8, 16 + H0 >= 1 + + An example gemm config file looks like: + 4,4,4,1 + 4,4,4,3 + ... + +EOF +} + +####################################### +# Print usage of this program and exit with Error +# Globals: +# Assumes all globals are required +# Arguments: +# None +# Returns: +# Error(1) +####################################### +function usage() { + cat >&2 << EOF +Run gemm examples of a selected strategy, over provided tunable configurationsa and gemm shapes. +Save the benchmark results to json files in an output directory. + +Usage: ${CMD} [-h] -e -g -c [-s , [-o ]] + +Options: + -h + Print help messages for selected , which is ${STRATEGY_OPTION} + + -e + Path to directory that holds all example binaries + + -g + Path to gemm shape csv file + + -c + Path to gemm config csv file + + -s + Strategy option. + Options: ${ALL_STRATEGY_OPTIONS}. Default: ${DEFAULT_STRATEGY_OPTION} + + -o + Path to output directory that holds output json files + Default: ${DEFAULT_OUT_DIR} + +EOF +# Print help messages about gemm shapes and various gemm configs +$HELP && help_gemm_shape_file +$HELP && ( [ "${STRATEGY_OPTION}" == "" ] || [ "${STRATEGY_OPTION}" == "reshaped_rhs_only" ] ) && help_gemm_config_file_reshaped_rhs_only +exit 1 +} + +####################################### +# Print error message and exit with Error. +# Globals: +# None +# Arguments: +# $1 - Error message +# Returns: +# None +####################################### +function error_msg() { + echo "Error: $1" 1>&2 + exit 1 +} + +####################################### +# Convert string to lower-case +# Globals: +# None +# Arguments: +# target - String +# Returns: +# (stdout) - String in lowercase +####################################### +function to_lower() { + local target=$1 + echo "$target" | tr '[:upper:]' '[:lower:]' +} + +####################################### +# Test if the argument is an integer +# Globals: +# None +# Arguments: +# in - Input +# Returns: +# true/false +####################################### +function is_integer() { + local in=$1 + [ "$in" -eq "$in" ] 2> /dev/null +} + +####################################### +# Run all tunable configurations and all input configurations +# Globals: +# OUT_DIR +# EXAMPLE_BIN_DIR +# NUM_ITERATION +# GEMM_CONFIGS_FILE +# GEMM_SHAPES_FILE +# Arguments: +# example_bin Name of the example binary to run +# Returns: +# None +####################################### +function run() { + local example_bin=$1 + echo "Running all configs for ${example_bin}" 1>&2 + local example_args + local test_id=1 + while read gemm_shape + do + while read gemm_config + do + example_args="${gemm_shape},${gemm_config}" + ${EXAMPLE_BIN_DIR}/${example_bin} --example_args=${example_args} --iterations=${NUM_ITERATION} --json-file=${OUT_DIR}/${test_id} + (( test_id++ )) + done < "${GEMM_CONFIGS_FILE}" + done < "${GEMM_SHAPES_FILE}" + echo "Finished running all configs for ${example_bin}" 1>&2 + echo "All results saved to ${OUT_DIR}" 1>&2 +} + +# Functions }}} + +# Main: Main script {{{ +# Path to directory containing all benchmark examples binaries +EXAMPLE_BIN_DIR="" +# Path to gemm shapes file +GEMM_SHAPES_FILE="" +# Path to gemm configs file +GEMM_CONFIGS_FILE="" +STRATEGY_OPTION=${DEFAULT_STRATEGY_OPTION} +# Path to output directory +OUT_DIR=${DEFAULT_OUT_DIR} +# Toggle help +HELP=false + +# Obtain options +while getopts "he:s:g:c:o:" opt; do + case "$opt" in + h|\?) HELP=true ;; + e) EXAMPLE_BIN_DIR="${OPTARG}";; + s) STRATEGY_OPTION=$(to_lower "${OPTARG}");; + g) GEMM_SHAPES_FILE="${OPTARG}";; + c) GEMM_CONFIGS_FILE="${OPTARG}";; + o) OUT_DIR="${OPTARG}";; + esac +done +shift $((OPTIND - 1)) + +# Lazily print usage (after arguments have been parsed) +$HELP && + usage + +# Parse and validate options +# Verify all arguments are passed in +[ ${OPTIND} -ge 7 ] || + usage + +# Verify example binaries directory exists +[ -d "${EXAMPLE_BIN_DIR}" ] || + error_msg "${EXAMPLE_BIN_DIR} does not exist." + +# Verify all benchmark example binaries exist +[ -f "${EXAMPLE_BIN_DIR}/${EXAMPLE_BIN_RESHAPED_RHS_ONLY}" ] || + error_msg "Cannot find ${EXAMPLE_BIN_RESHAPED_RHS_ONLY} at ${EXAMPLE_BIN_DIR}" + +# Verify Gemm shapes file exists +[ -f "${GEMM_SHAPES_FILE}" ] || + error_msg "Cannot find gemm shapes file ${GEMM_SHAPES_FILE}" + +# Verify Gemm shapes file exists +[ -f "${GEMM_CONFIGS_FILE}" ] || + error_msg "Cannot find gemm configs file ${GEMM_CONFIGS_FILE}" + +# Verify strategy option is valid +[[ "${ALL_STRATEGY_OPTIONS}" == *"${STRATEGY_OPTION}"* ]] || + error_msg "Does not support strategy ${STRATEGY_OPTION}" + +# Make sure existing benchmark outputs are not overwritten +[ ! -d "${OUT_DIR}" ] || + error_msg "Output directory ${OUT_DIR} already exists!" + +# Make output directory +mkdir ${OUT_DIR} + +# Run selected strategy with all configurations +[ "${STRATEGY_OPTION}" == "reshaped_rhs_only" ] && run $EXAMPLE_BIN_RESHAPED_RHS_ONLY +# Main: Main script }}} diff --git a/examples/gemm_tuner/cl_gemm_reshaped_rhs_only.cpp b/examples/gemm_tuner/cl_gemm_reshaped_rhs_only.cpp new file mode 100644 index 0000000000..5075c1daf9 --- /dev/null +++ b/examples/gemm_tuner/cl_gemm_reshaped_rhs_only.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2019 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_CL /* Needed by Utils.cpp to handle OpenCL exceptions properly */ +#error "This example needs to be built with -DARM_COMPUTE_CL" +#endif /* ARM_COMPUTE_CL */ + +#include "arm_compute/core/CL/kernels/CLGEMMMatrixMultiplyReshapedOnlyRHSKernel.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/KernelDescriptors.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" +#include "arm_compute/runtime/CL/CLFunctions.h" +#include "arm_compute/runtime/CL/CLScheduler.h" +#include "arm_compute/runtime/CL/CLTuner.h" +#include "tests/CL/Helper.h" +#include "utils/Utils.h" + +#include + +using namespace arm_compute; +using namespace utils; +using namespace arm_compute::misc::shape_calculator; + +namespace +{ +/** Structure holding all the common gemm example parameters */ +struct CommonGemmExampleParams +{ + size_t M{ 100 }; + size_t N{ 100 }; + size_t K{ 50 }; + size_t B{ 1 }; +}; + +/** Formatted output of the CommonGemmExampleParams type + * + * @param[out] os Output stream. + * @param[in] common_params Common parameters to output + * + * @return Modified output stream. + */ +::std::ostream &operator<<(::std::ostream &os, const CommonGemmExampleParams &common_params) +{ + os << "M : " << common_params.M << std::endl; + os << "N : " << common_params.N << std::endl; + os << "K : " << common_params.K << std::endl; + os << "B : " << common_params.B << std::endl; + return os; +} + +/** Structure holding all tunable gemm configs specific to this example/strategy */ +struct GemmConfigs +{ + size_t m0{ 4 }; + size_t n0{ 4 }; + size_t k0{ 4 }; + size_t h0{ 1 }; + bool interleave_rhs{ true }; + bool transpose_rhs{ true }; +}; + +/** Formatted output of the GemmConfigs type + * + * @param[out] os Output stream. + * @param[in] configs Tunable configurations to output + * + * @return Modified output stream. + */ +::std::ostream &operator<<(::std::ostream &os, const GemmConfigs &configs) +{ + std::string false_str = std::string("false"); + std::string true_str = std::string("true"); + + os << "m0 : " << configs.m0 << std::endl; + os << "n0 : " << configs.n0 << std::endl; + os << "k0 : " << configs.k0 << std::endl; + os << "h0 : " << configs.h0 << std::endl; + os << "interleave_rhs : " << (configs.interleave_rhs ? true_str : false_str) << std::endl; + os << "transpose_rhs : " << (configs.transpose_rhs ? true_str : false_str) << std::endl; + return os; +} +} // namespace +// Create function for CLGEMMMatrixMultiplyReshapedOnlyRHSKernel +using CLGEMMMatrixMultiplyReshapedOnlyRHS = test::CLSynthetizeFunction; + +class CLGEMMMatrixMultiplyReshapedOnlyRHSExample : public Example +{ +public: + bool do_setup(int argc, char **argv) override + { + // Default parameters + const DataType data_type = DataType::F32; + const float alpha = 1.0f; + const float beta = 0.0f; + const ActivationLayerInfo act_info = ActivationLayerInfo(); + CommonGemmExampleParams params; + GemmConfigs configs; + if(argc < 9 || argc > 11) + { + // Print help + // Use default parameters + std::cerr << "Usage: ./build/cl_gemm_reshaped_rhs_only M N K B m0 n0 k0 h0 [interleave_rhs = 1] [transpose_rhs = 1]\n\n"; + std::cerr << "Falling back to default parameters and configs" << std::endl; + } + else + { + // Set parameters from command line arguments + params.M = strtol(argv[1], nullptr, 10); + params.N = strtol(argv[2], nullptr, 10); + params.K = strtol(argv[3], nullptr, 10); + params.B = strtol(argv[4], nullptr, 10); + configs.m0 = strtol(argv[5], nullptr, 10); + configs.n0 = strtol(argv[6], nullptr, 10); + configs.k0 = strtol(argv[7], nullptr, 10); + configs.h0 = strtol(argv[8], nullptr, 10); + if(argc > 9) + { + configs.interleave_rhs = strtol(argv[9], nullptr, 10) == 1; + } + if(argc > 10) + { + configs.transpose_rhs = strtol(argv[10], nullptr, 10) == 1; + } + } + std::cerr << "Gemm parameters:" << std::endl; + std::cerr << params << std::endl; + std::cerr << "Gemm configurations:" << std::endl; + std::cerr << configs << std::endl; + + CLScheduler::get().default_init(&tuner); + + lhs.allocator()->init(TensorInfo(TensorShape(params.K, params.M, params.B), 1, data_type)); + rhs.allocator()->init(TensorInfo(TensorShape(params.N, params.K, params.B), 1, data_type)); + bias.allocator()->init(TensorInfo(TensorShape(params.N, params.M, params.B), 1, data_type)); + + init_sgemm_output(dst, lhs, rhs, data_type); + + GEMMLHSMatrixInfo lhs_info; + lhs_info.m0 = configs.m0; + lhs_info.k0 = configs.k0; + + GEMMRHSMatrixInfo rhs_info; + rhs_info.n0 = configs.n0; + rhs_info.k0 = configs.k0; + rhs_info.h0 = configs.h0; + rhs_info.interleave = configs.interleave_rhs; + rhs_info.transpose = configs.transpose_rhs; + + GEMMKernelInfo kernel_info; + kernel_info.m = params.M; + kernel_info.n = params.N; + kernel_info.k = params.K; + kernel_info.depth_output_gemm3d = 0; + kernel_info.reinterpret_input_as_3d = false; + kernel_info.broadcast_bias = true; + kernel_info.activation_info = act_info; + + // Initialise rhs_reshaped tensor info + auto_init_if_empty(*rhs_reshaped.info(), rhs.info()->clone()->set_tensor_shape(compute_rhs_reshaped_shape(*rhs.info(), rhs_info))); + + // Configure function + gemm.configure(&lhs, &rhs_reshaped, &bias, &dst, alpha, beta, lhs_info, rhs_info, kernel_info); + + // Allocate tensors + lhs.allocator()->allocate(); + rhs.allocator()->allocate(); + rhs_reshaped.allocator()->allocate(); + bias.allocator()->allocate(); + dst.allocator()->allocate(); + + return true; + } + void do_run() override + { + // Execute the function + gemm.run(); + + // Make sure all the OpenCL jobs are done executing: + CLScheduler::get().sync(); + } + + void do_teardown() override + { + } + +private: + CLTensor lhs{}; + CLTensor rhs{}; + CLTensor rhs_reshaped{}; + CLTensor bias{}; + CLTensor dst{}; + CLTuner tuner{}; + CLGEMMMatrixMultiplyReshapedOnlyRHS gemm{}; +}; + +/** Main program for gemm reshaped rhs only test + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( M, N, K, B, m0, n0, k0, h0, [optional] interleave_rhs, [optional] transpose_rhs ) + */ +int main(int argc, char **argv) +{ + return utils::run_example(argc, argv); +} -- cgit v1.2.1