// Copyright (c) Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #ifndef GUARD_MIOPEN_SOFTMAX_DRIVER_HPP #define GUARD_MIOPEN_SOFTMAX_DRIVER_HPP #include "InputFlags.hpp" #include "driver.hpp" #include "mloSoftmaxHost.hpp" #include "random.hpp" #include "tensor_driver.hpp" #include "timer.hpp" #include "util_driver.hpp" #include <../test/verify.hpp> #include #include #include #include #include #include template class SoftmaxDriver : public Driver { public: SoftmaxDriver() : Driver() { miopenCreateTensorDescriptor(&inputTensor); miopenCreateTensorDescriptor(&outputTensor); miopenCreateTensorDescriptor(&dInputTensor); miopenCreateTensorDescriptor(&dOutputTensor); data_type = std::is_same_v ? miopenFloat : std::is_same_v ? miopenHalf : miopenBFloat16; } int AddCmdLineArgs() override; int ParseCmdLineArgs(int argc, char* argv[]) override; InputFlags& GetInputFlags() override { return inflags; } int GetandSetData() override; std::vector GetInputTensorLengthsFromCmdLine(); void ValidateLayout(); int AllocateBuffersAndCopy() override; int RunForwardGPU() override; int RunForwardCPU(); int RunBackwardGPU() override; int RunBackwardCPU(); Tref GetTolerance(); int VerifyBackward() override; int VerifyForward() override; ~SoftmaxDriver() override { miopenDestroyTensorDescriptor(outputTensor); miopenDestroyTensorDescriptor(inputTensor); miopenDestroyTensorDescriptor(dOutputTensor); miopenDestroyTensorDescriptor(dInputTensor); } private: InputFlags inflags; miopenTensorDescriptor_t inputTensor; miopenTensorDescriptor_t outputTensor; std::unique_ptr in_dev; std::unique_ptr out_dev; std::vector in; std::vector out; std::vector outhost; miopenTensorDescriptor_t dInputTensor; miopenTensorDescriptor_t dOutputTensor; std::unique_ptr din_dev; std::unique_ptr dout_dev; std::vector din; std::vector dout; std::vector dinhost; float alpha; float beta; miopenSoftmaxAlgorithm_t algo; miopenSoftmaxMode_t mode; bool isForward = false; }; template int SoftmaxDriver::ParseCmdLineArgs(int argc, char* argv[]) { inflags.Parse(argc, argv); if(inflags.GetValueInt("time") == 1) { miopenEnableProfiling(GetHandle(), true); } return miopenStatusSuccess; } template int SoftmaxDriver::GetandSetData() { std::vector in_len = GetInputTensorLengthsFromCmdLine(); ValidateLayout(); if(SetTensorNd(inputTensor, in_len, inflags.GetValueStr("layout"), data_type) != miopenStatusSuccess) { MIOPEN_THROW("Error setting input tensor."); } if(SetTensorNd(outputTensor, in_len, inflags.GetValueStr("layout"), data_type) != miopenStatusSuccess) { MIOPEN_THROW("Error setting output tensor."); } if(SetTensorNd(dInputTensor, in_len, inflags.GetValueStr("layout"), data_type) != miopenStatusSuccess) { MIOPEN_THROW("Error setting dinput tensor."); } if(SetTensorNd(dOutputTensor, in_len, inflags.GetValueStr("layout"), data_type) != miopenStatusSuccess) { MIOPEN_THROW("Error setting doutput tensor."); } alpha = static_cast(inflags.GetValueDouble("alpha")); beta = static_cast(inflags.GetValueDouble("beta")); algo = miopenSoftmaxAlgorithm_t(inflags.GetValueInt("algorithm")); mode = miopenSoftmaxMode_t(inflags.GetValueInt("mode")); return (0); } template int SoftmaxDriver::AddCmdLineArgs() { inflags.AddInputFlag("forw", 'F', "0", "Run only Forward Softmax (Default=0)", "int"); inflags.AddInputFlag( "layout", 'L', "", "Tensor layout: [NCHW, NHWC] (Default=NCHW)", "string", true); inflags.AddInputFlag("batchsize", 'n', "100", "Mini-batch size (Default=100)", "int"); inflags.AddInputFlag("in_channels", 'c', "3", "Number of Input Channels (Default=3)", "int"); inflags.AddInputFlag("in_h", 'H', "32", "Input Height (Default=32)", "int"); inflags.AddInputFlag("in_w", 'W', "32", "Input Width (Default=32)", "int"); inflags.AddInputFlag("alpha", 'A', "1.0", "Softmax shift (Default=1.0)", "float"); inflags.AddInputFlag("beta", 'B', "0.0", "Softmax scale (Default=0.0)", "float"); inflags.AddInputFlag("algorithm", 'a', "1", "softmax algorithms: fast (0), accurate (1), logsoftmax (2) (Default=1)", "int"); inflags.AddInputFlag( "mode", 'm', "1", "instance mode (0), channel mode (1) (Default=1)", "int"); inflags.AddInputFlag("iter", 'i', "10", "Number of Iterations (Default=10)", "int"); inflags.AddInputFlag("verify", 'V', "1", "Verify Each Layer (Default=1)", "int"); inflags.AddInputFlag("time", 't', "0", "Time Each Layer (Default=0)", "int"); inflags.AddInputFlag( "wall", 'w', "0", "Wall-clock Time Each Layer, Requires time == 1 (Default=0)", "int"); return miopenStatusSuccess; } template std::vector SoftmaxDriver::GetInputTensorLengthsFromCmdLine() { int in_n = inflags.GetValueInt("batchsize"); int in_c = inflags.GetValueInt("in_channels"); int in_h = inflags.GetValueInt("in_h"); int in_w = inflags.GetValueInt("in_w"); isForward = inflags.GetValueInt("forw") == 1; return std::vector({in_n, in_c, in_h, in_w}); } template void SoftmaxDriver::ValidateLayout() { auto layout_value = inflags.GetValueStr("layout"); if(layout_value.empty()) { inflags.SetValue("layout", "NCHW"); } else if(layout_value != "NCHW" && layout_value != "NHWC") { MIOPEN_THROW(miopenStatusInvalidValue, "Invalid layout parameter value: " + layout_value); } } template int SoftmaxDriver::AllocateBuffersAndCopy() { size_t in_sz = GetTensorSize(inputTensor); size_t out_sz = GetTensorSize(outputTensor); DEFINE_CONTEXT(ctx); in_dev = std::unique_ptr(new GPUMem(ctx, in_sz, sizeof(Tgpu))); out_dev = std::unique_ptr(new GPUMem(ctx, out_sz, sizeof(Tgpu))); din_dev = std::unique_ptr(new GPUMem(ctx, in_sz, sizeof(Tgpu))); dout_dev = std::unique_ptr(new GPUMem(ctx, out_sz, sizeof(Tgpu))); in = std::vector(in_sz, static_cast(0)); out = std::vector(out_sz, static_cast(0)); outhost = std::vector(out_sz, static_cast(0)); din = std::vector(in_sz, static_cast(0)); dout = std::vector(out_sz, static_cast(0)); dinhost = std::vector(in_sz, static_cast(0)); for(int i = 0; i < in_sz; i++) { in[i] = prng::gen_canonical(); } const Tgpu Data_scale = static_cast(0.001); for(int i = 0; i < out_sz; i++) { dout[i] = Data_scale * prng::gen_A_to_B(static_cast(-0.5), static_cast(0.5)); } // if bwd then initialize the y if(!isForward) { for(int i = 0; i < out_sz; i++) { out[i] = prng::gen_A_to_B(static_cast(-0.6), static_cast(0.6)); } } status_t status; status = in_dev->ToGPU(q, in.data()); status |= out_dev->ToGPU(q, out.data()); status |= din_dev->ToGPU(q, din.data()); status |= dout_dev->ToGPU(q, dout.data()); if(status != STATUS_SUCCESS) printf("Error copying data to GPU\n"); return miopenStatusSuccess; } template int SoftmaxDriver::RunForwardGPU() { float kernel_total_time = 0.0; float kernel_first_time = 0.0; float wall_first_time = 0.0; Timer t; START_TIME for(int i = 0; i < inflags.GetValueInt("iter"); i++) { miopenSoftmaxForward_V2(GetHandle(), &alpha, inputTensor, in_dev->GetMem(), &beta, outputTensor, out_dev->GetMem(), algo, mode); float time = 0.0; miopenGetKernelTime(GetHandle(), &time); kernel_total_time += time; if(i == 0) { kernel_first_time = time; STOP_TIME wall_first_time = t.gettime_ms(); START_TIME } } if(inflags.GetValueInt("time") == 1) { STOP_TIME int iter = inflags.GetValueInt("iter"); auto gpu_time = kernel_first_time; auto wall_time = wall_first_time; auto aux_wall_time = 0.0f; if(iter > 1) { gpu_time = (kernel_total_time - kernel_first_time) / (iter - 1); wall_time = t.gettime_ms() / (iter - 1); aux_wall_time = wall_first_time - wall_time; } if(WALL_CLOCK) { printf("Wall-clock Time Forward Softmax Elapsed: %f ms", wall_time); if(iter > 1) printf(", First Call Overhead: %f ms", aux_wall_time); printf("\n"); } printf("GPU Kernel Time Forward Softmax Elapsed: %f ms\n", gpu_time); } out_dev->FromGPU(GetStream(), out.data()); return miopenStatusSuccess; } template int SoftmaxDriver::RunForwardCPU() { return (0); } template int SoftmaxDriver::RunBackwardGPU() { float kernel_total_time = 0.0; float kernel_first_time = 0.0; float wall_first_time = 0.0; Timer t; START_TIME for(int i = 0; i < inflags.GetValueInt("iter"); i++) { miopenSoftmaxBackward_V2(GetHandle(), &alpha, outputTensor, out_dev->GetMem(), dOutputTensor, dout_dev->GetMem(), &beta, dInputTensor, din_dev->GetMem(), algo, mode); float time = 0.0; miopenGetKernelTime(GetHandle(), &time); kernel_total_time += time; if(i == 0) { kernel_first_time = time; STOP_TIME wall_first_time = t.gettime_ms(); START_TIME } } if(inflags.GetValueInt("time") == 1) { STOP_TIME int iter = inflags.GetValueInt("iter"); auto gpu_time = kernel_first_time; auto wall_time = wall_first_time; auto aux_wall_time = 0.0f; if(iter > 1) { gpu_time = (kernel_total_time - kernel_first_time) / (iter - 1); wall_time = t.gettime_ms() / (iter - 1); aux_wall_time = wall_first_time - wall_time; } if(WALL_CLOCK) { printf("Wall-clock Time Backward Softmax Elapsed: %f ms", wall_time); if(iter > 1) printf(", First Call Overhead: %f ms", aux_wall_time); printf("\n"); } printf("GPU Kernel Time Backward Softmax Elapsed: %f ms\n", gpu_time); } din_dev->FromGPU(GetStream(), din.data()); return miopenStatusSuccess; } template Tref SoftmaxDriver::GetTolerance() { auto tolerance = data_type == miopenFloat ? 1e-3 : 5e-2; // bf16 mantissa has 7 bits, by 3 bits shorter than fp16. if(std::is_same::value) tolerance *= 8.0; return tolerance; } template int SoftmaxDriver::VerifyForward() { mloSoftmaxForwardRunHost( inputTensor, outputTensor, in.data(), outhost.data(), alpha, beta, algo, mode); auto error = miopen::rms_range(outhost, out); const Tref tolerance = GetTolerance(); if(!std::isfinite(error) || error > tolerance) { std::cout << "Forward Softmax FAILED: " << error << std::endl; } else { printf("Forward Softmax Verifies on CPU and GPU (err=%f)\n", error); } return miopenStatusSuccess; } template int SoftmaxDriver::RunBackwardCPU() { return miopenStatusSuccess; } template int SoftmaxDriver::VerifyBackward() { mloSoftmaxBackwardRunHost(inputTensor, outputTensor, out.data(), dout.data(), dinhost.data(), alpha, beta, algo, mode); auto error = miopen::rms_range(dinhost, din); const Tref tolerance = GetTolerance(); if(!std::isfinite(error) || error > tolerance) { std::cout << "Backward Softmax FAILED: " << error << std::endl; } else { printf("Backward Softmax Verifies on CPU and GPU (err=%f)\n", error); } return miopenStatusSuccess; } #endif // GUARD_MIOPEN_SOFTMAX_DRIVER_HPP