mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
Add op tuning functionality and an example for vector add. (#12060)
* Add op tuning functionality and example for vector add. * Add namespace. * Various improvements. * use unique pointer * fix lint errors * Check return error.
This commit is contained in:
parent
df712d80ca
commit
ddb6202df7
12 changed files with 251 additions and 38 deletions
49
onnxruntime/contrib_ops/rocm/bert/timer.cc
Normal file
49
onnxruntime/contrib_ops/rocm/bert/timer.cc
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "contrib_ops/rocm/bert/timer.h"
|
||||
|
||||
#define HIP_CHECKED_CALL(expr) \
|
||||
do { \
|
||||
auto status = expr; \
|
||||
if (status != hipSuccess) { \
|
||||
std::printf("HIP Error at %s:%d\n Error name : %s\n Error string: %s\n", \
|
||||
__FILE__, __LINE__, hipGetErrorName(status), hipGetErrorString(status)); \
|
||||
std::abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
namespace rocm {
|
||||
|
||||
Timer::Timer() {
|
||||
HIP_CHECKED_CALL(hipEventCreate(&start_));
|
||||
HIP_CHECKED_CALL(hipEventCreate(&end_));
|
||||
}
|
||||
|
||||
void Timer::Start() {
|
||||
HIP_CHECKED_CALL(hipDeviceSynchronize());
|
||||
HIP_CHECKED_CALL(hipEventRecord(start_, nullptr));
|
||||
}
|
||||
|
||||
void Timer::End() {
|
||||
HIP_CHECKED_CALL(hipEventRecord(end_, nullptr));
|
||||
HIP_CHECKED_CALL(hipEventSynchronize(end_));
|
||||
}
|
||||
|
||||
float Timer::time() {
|
||||
float time;
|
||||
// time is in ms with a resolution of 1 us
|
||||
HIP_CHECKED_CALL(hipEventElapsedTime(&time, start_, end_));
|
||||
return time;
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
HIP_CHECKED_CALL(hipEventDestroy(start_));
|
||||
HIP_CHECKED_CALL(hipEventDestroy(end_));
|
||||
}
|
||||
|
||||
} // namespace rocm
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
#include "hip/hip_runtime.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
namespace rocm {
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
|
|
@ -16,3 +20,7 @@ class Timer {
|
|||
private:
|
||||
hipEvent_t start_, end_;
|
||||
};
|
||||
|
||||
} // namespace rocm
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
91
onnxruntime/contrib_ops/rocm/bert/tunable_op.h
Normal file
91
onnxruntime/contrib_ops/rocm/bert/tunable_op.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "contrib_ops/rocm/bert/timer.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace contrib {
|
||||
namespace rocm {
|
||||
|
||||
struct OpParams {
|
||||
virtual std::string signature() const = 0;
|
||||
};
|
||||
|
||||
class Op {
|
||||
public:
|
||||
Op() : repeats_(100) {}
|
||||
|
||||
virtual void Run(const OpParams*) = 0;
|
||||
|
||||
void SetRepeats(int n) {
|
||||
repeats_ = n;
|
||||
}
|
||||
|
||||
float Profile(const OpParams* op_params) {
|
||||
// warm up
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Run(op_params);
|
||||
}
|
||||
timer_.Start();
|
||||
for (int i = 0; i < repeats_; i++) {
|
||||
Run(op_params);
|
||||
}
|
||||
timer_.End();
|
||||
return timer_.time()/repeats_;
|
||||
}
|
||||
|
||||
virtual ~Op() {}
|
||||
|
||||
private:
|
||||
Timer timer_;
|
||||
int repeats_;
|
||||
};
|
||||
|
||||
class TunableOp {
|
||||
public:
|
||||
TunableOp() {}
|
||||
|
||||
void Run(const OpParams* op_params_) {
|
||||
int id;
|
||||
if (kernel_map_.find(op_params_->signature()) == kernel_map_.end()) {
|
||||
id = FindFastest(op_params_);
|
||||
kernel_map_.insert({op_params_->signature(), id});
|
||||
} else {
|
||||
id = kernel_map_[op_params_->signature()];
|
||||
}
|
||||
ops_[id]->Run(op_params_);
|
||||
}
|
||||
|
||||
virtual ~TunableOp() {}
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<Op>> ops_;
|
||||
|
||||
private:
|
||||
int FindFastest(const OpParams* op_params_) {
|
||||
assert(ops_.size() > 0);
|
||||
float min_time = ops_[0]->Profile(op_params_);
|
||||
int id = 0;
|
||||
for (int i = 1; i < ops_.size(); i++) {
|
||||
float time = ops_[i]->Profile(op_params_);
|
||||
if (time < min_time) {
|
||||
min_time = time;
|
||||
id = i;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
std::map<std::string, int> kernel_map_;
|
||||
};
|
||||
|
||||
} // namespace rocm
|
||||
} // namespace contrib
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -18,13 +18,15 @@ list(APPEND CMAKE_PREFIX_PATH ${onnxruntime_ROCM_HOME} ${onnxruntime_ROCM_HOME}/
|
|||
find_package(hip)
|
||||
find_package(PythonLibs 3.8 EXACT REQUIRED)
|
||||
|
||||
set(onnxruntime_DIR ../../../../onnxruntime/)
|
||||
set(onnxruntime_BERT_DIR ${onnxruntime_DIR}/contrib_ops/rocm/bert/)
|
||||
include_directories(${PYTHON_INCLUDE_DIRS})
|
||||
include_directories(${pybind11_INCLUDE_DIRS})
|
||||
include_directories(../../../../onnxruntime/contrib_ops/rocm/bert/)
|
||||
include_directories(${onnxruntime_DIR})
|
||||
include_directories(.)
|
||||
|
||||
FILE(GLOB kernel_srcs kernels/*.cpp)
|
||||
add_library(kernel_explorer SHARED kernel_explorer.cpp timer.cpp ${kernel_srcs})
|
||||
FILE(GLOB kernel_srcs kernels/*.cc)
|
||||
add_library(kernel_explorer SHARED kernel_explorer.cc ${onnxruntime_BERT_DIR}/timer.cc ${kernel_srcs})
|
||||
target_link_libraries(kernel_explorer ${PYTHON_LIBRARIES})
|
||||
set_target_properties(kernel_explorer PROPERTIES PREFIX "")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Kernel Explorer
|
||||
Kernel Explorer is a tool to help develop, test, and profile GPU kernels.
|
||||
Kernel Explorer hooks up GPU kernel code with a Python frontend to help develop, test, profile, and auto-tune GPU kernels.
|
||||
|
||||
## Example Usage
|
||||
```
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "device_array.h"
|
||||
#include "operator.h"
|
||||
#include "fast_gelu_impl_kernel.h"
|
||||
#include "contrib_ops/rocm/bert/fast_gelu_impl_kernel.h"
|
||||
|
||||
template <typename T, int ThreadsPerBlock, int VecSize>
|
||||
void LaunchFastGelu(const T* input, const T* bias, T* output, int input_length, int bias_length) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include "hip/hip_fp16.h"
|
||||
#include "vector_add_kernel.h"
|
||||
#include "kernels/vector_add_kernel.h"
|
||||
#include "kernels/vector_add_tunable_op.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
|
|
@ -28,6 +29,29 @@ class VectorAdd: public Operator<T> {
|
|||
int n_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class VectorAddTunable: public Operator<T> {
|
||||
public:
|
||||
VectorAddTunable(DeviceArray& x, DeviceArray& y, DeviceArray& z, int n) :
|
||||
x_(reinterpret_cast<T*>(x.ptr())),
|
||||
y_(reinterpret_cast<T*>(y.ptr())),
|
||||
z_(reinterpret_cast<T*>(z.ptr())),
|
||||
n_(n),
|
||||
Operator<T>() {}
|
||||
|
||||
void Run() {
|
||||
VectorAddParams<T> op_params(x_, y_, z_, n_);
|
||||
op_.Run(&op_params);
|
||||
}
|
||||
|
||||
private:
|
||||
T* x_;
|
||||
T* y_;
|
||||
T* z_;
|
||||
int n_;
|
||||
VectorAddTunableOp<T> op_;
|
||||
};
|
||||
|
||||
#define REGISTER_OP(name, type, threads_per_block, vec_size) \
|
||||
py::class_<name<type, threads_per_block, vec_size>>(m, #name"_"#type"_"#threads_per_block"_"#vec_size) \
|
||||
.def(py::init<DeviceArray&, DeviceArray&, DeviceArray&, int>()) \
|
||||
|
|
@ -55,4 +79,9 @@ class VectorAdd: public Operator<T> {
|
|||
void InitVectorAdd(py::module m) {
|
||||
REGISTER_OP_FOR_ALL_THREADS_PER_BLOCK(VectorAdd, half);
|
||||
REGISTER_OP_FOR_ALL_THREADS_PER_BLOCK(VectorAdd, float);
|
||||
py::class_<VectorAddTunable<half>>(m, "VectorAdd_half_Tunable")
|
||||
.def(py::init<DeviceArray&, DeviceArray&, DeviceArray&, int>())
|
||||
.def("SetRepeats", &VectorAddTunable<half>::SetRepeats)
|
||||
.def("Profile", &VectorAddTunable<half>::Profile)
|
||||
.def("Run", &VectorAddTunable<half>::Run);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "contrib_ops/rocm/bert/tunable_op.h"
|
||||
#include "kernels/vector_add_kernel.h"
|
||||
|
||||
using onnxruntime::contrib::rocm::OpParams;
|
||||
using onnxruntime::contrib::rocm::Op;
|
||||
using onnxruntime::contrib::rocm::TunableOp;
|
||||
|
||||
template<typename T>
|
||||
struct VectorAddParams : OpParams {
|
||||
VectorAddParams(const T* x, const T* y, T* z, int n) : x(x), y(y), z(z), n(n) {}
|
||||
|
||||
std::string signature() const {
|
||||
return std::to_string(n);
|
||||
}
|
||||
|
||||
const T* x;
|
||||
const T* y;
|
||||
T* z;
|
||||
int n;
|
||||
};
|
||||
|
||||
template <typename T, int ThreadsPerBlock, int VecSize>
|
||||
class VectorAddOp : public Op {
|
||||
public:
|
||||
VectorAddOp() : Op() {}
|
||||
|
||||
void Run(const OpParams* op_params) {
|
||||
const VectorAddParams<T>* vector_add_params = static_cast<const VectorAddParams<T>*>(op_params);
|
||||
LaunchVectorAdd<T, ThreadsPerBlock, VecSize>(vector_add_params->x,
|
||||
vector_add_params->y,
|
||||
vector_add_params->z,
|
||||
vector_add_params->n);
|
||||
}
|
||||
};
|
||||
|
||||
#define ADD_OP(threads_per_block) \
|
||||
ops_.push_back(std::make_unique<VectorAddOp<half, threads_per_block, 1>>()); \
|
||||
ops_.push_back(std::make_unique<VectorAddOp<half, threads_per_block, 2>>()); \
|
||||
ops_.push_back(std::make_unique<VectorAddOp<half, threads_per_block, 4>>()); \
|
||||
ops_.push_back(std::make_unique<VectorAddOp<half, threads_per_block, 8>>());
|
||||
|
||||
template <typename T>
|
||||
class VectorAddTunableOp : public TunableOp {
|
||||
public:
|
||||
VectorAddTunableOp() {
|
||||
ADD_OP(64);
|
||||
ADD_OP(128);
|
||||
ADD_OP(192);
|
||||
ADD_OP(256);
|
||||
ADD_OP(320);
|
||||
ADD_OP(384);
|
||||
ADD_OP(448);
|
||||
ADD_OP(512);
|
||||
}
|
||||
};
|
||||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "timer.h"
|
||||
#include "contrib_ops/rocm/bert/timer.h"
|
||||
|
||||
using onnxruntime::contrib::rocm::Timer;
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace rocm {
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
Timer::Timer() {
|
||||
hipEventCreate(&start_);
|
||||
hipEventCreate(&end_);
|
||||
}
|
||||
|
||||
void Timer::Start() {
|
||||
hipDeviceSynchronize();
|
||||
hipEventRecord(start_, nullptr);
|
||||
}
|
||||
|
||||
void Timer::End() {
|
||||
hipEventRecord(end_, nullptr);
|
||||
hipEventSynchronize(end_);
|
||||
}
|
||||
|
||||
float Timer::time() {
|
||||
float time;
|
||||
// time is in ms with a resolution of 1 us
|
||||
hipEventElapsedTime(&time, start_, end_);
|
||||
return time;
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
hipEventDestroy(start_);
|
||||
hipEventDestroy(end_);
|
||||
}
|
||||
Loading…
Reference in a new issue