Files
apache--tvm/include/tvm/runtime/timer.h
T
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

195 lines
7.4 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file include/tvm/runtime/timer.h
* \brief Runtime timer primitives: Timer, TimerNode, WrapTimeEvaluator.
*/
#ifndef TVM_RUNTIME_TIMER_H_
#define TVM_RUNTIME_TIMER_H_
#include <tvm/ffi/function.h>
#include <tvm/runtime/base.h>
#include <tvm/runtime/device_api.h>
#include <tvm/runtime/tensor.h>
namespace tvm {
namespace runtime {
/*! \brief Base class for all timer implementations.
*
* New implementations of this interface should make sure that `Start` and `Stop`
* are as lightweight as possible. Expensive state synchronization should be
* done in `SyncAndGetElapsedNanos`.
*/
class TimerNode : public ffi::Object {
public:
/*! \brief Start the timer.
*
* Note: this function should only be called once per object.
*/
virtual void Start() = 0;
/*! \brief Stop the timer.
*
* Note: this function should only be called once per object.
*/
virtual void Stop() = 0;
/*! \brief Synchronize timer state and return elapsed time between `Start` and `Stop`.
* \return The time in nanoseconds between `Start` and `Stop`.
*
* This function is necessary because we want to avoid timing the overhead of
* doing timing. When using multiple timers, it is recommended to stop all of
* them before calling `SyncAndGetElapsedNanos` on any of them.
*
* Note: this function should be only called once per object. It may incur
* a large synchronization overhead (for example, with GPUs).
*/
virtual int64_t SyncAndGetElapsedNanos() = 0;
virtual ~TimerNode() {}
static constexpr const bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("runtime.TimerNode", TimerNode, ffi::Object);
};
/*! \brief Timer for a specific device.
*
* This is a managed reference to a TimerNode.
*
* \sa TimerNode
*/
class Timer : public ffi::ObjectRef {
public:
/*!
* \brief Get a device specific timer.
* \param dev The device to time.
* \return A `Timer` that has already been started.
*
* Use this function to time runtime of arbitrary regions of code on a specific
* device. The code that you want to time should be running on the device
* otherwise the timer will not return correct results. This is a lower level
* interface than TimeEvaluator and only runs the timed code once
* (TimeEvaluator runs the code multiple times).
*
* A default timer is used if a device specific one does not exist. This
* timer performs synchronization between the device and CPU, which can lead
* to overhead in the reported results.
*
* Example usage:
* \code{.cpp}
* Timer t = Timer::Start(Device::cpu());
* my_long_running_function();
* t->Stop();
* ... // some more computation
* int64_t nanosecs = t->SyncAndGetElapsedNanos() // elapsed time in nanoseconds
* \endcode
*
* To add a new device-specific timer, register a new function
* "runtime.timer.my_device" (where `my_device` is the `DeviceName` of your
* device). This function should accept a `Device` and return a new `Timer`
* that has already been started.
*
* For example, this is how the CPU timer is implemented:
* \code{.cpp}
* class CPUTimerNode : public TimerNode {
* public:
* virtual void Start() { start_ = std::chrono::high_resolution_clock::now(); }
* virtual void Stop() { duration_ = std::chrono::high_resolution_clock::now() - start_; }
* virtual int64_t SyncAndGetElapsedNanos() { return duration_.count(); }
* virtual ~CPUTimerNode() {}
*
* static constexpr const char* _type_key = "runtime.CPUTimerNode";
* TVM_FFI_DECLARE_OBJECT_INFO_FINAL(CPUTimerNode, TimerNode);
*
* private:
* std::chrono::high_resolution_clock::time_point start_;
* std::chrono::duration<int64_t, std::nano> duration_;
* };
*
*
* TVM_FFI_STATIC_INIT_BLOCK() {
* namespace refl = tvm::ffi::reflection;
* refl::GlobalDef().def("runtime.timer.cpu", [](Device dev) {
* return Timer(ffi::make_object<CPUTimerNode>());
* });
* }
* \endcode
*/
static TVM_RUNTIME_DLL Timer Start(Device dev);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Timer, ffi::ObjectRef, TimerNode);
};
/*!
* \brief Wrap a timer function to measure the time cost of a given packed function.
*
* Approximate implementation:
* \code{.py}
* f() // warmup
* for i in range(repeat)
* f_preproc()
* while True:
* start = time()
* for j in range(number):
* f()
* duration_ms = time() - start
* if duration_ms >= min_repeat_ms:
* break
* else:
* number = (min_repeat_ms / (duration_ms / number) + 1
* if cooldown_interval_ms and i % repeats_to_cooldown == 0:
* sleep(cooldown_interval_ms)
* \endcode
*
* \param f The function argument.
* \param dev The device.
* \param number The number of times to run this function for taking average.
* We call these runs as one `repeat` of measurement.
* \param repeat The number of times to repeat the measurement.
* In total, the function will be invoked (1 + number x repeat) times,
* where the first one is warm up and will be discarded.
* The returned result contains `repeat` costs,
* each of which is an average of `number` costs.
* \param min_repeat_ms The minimum duration of one `repeat` in milliseconds.
* By default, one `repeat` contains `number` runs. If this parameter is set,
* the parameters `number` will be dynamically adjusted to meet the
* minimum duration requirement of one `repeat`.
* i.e., When the run time of one `repeat` falls below this time,
* the `number` parameter will be automatically increased.
* \param limit_zero_time_iterations The maximum number of repeats when
* measured time is equal to 0. It helps to avoid hanging during measurements.
* \param cooldown_interval_ms The cooldown interval in milliseconds between the number of repeats
* defined by `repeats_to_cooldown`.
* \param repeats_to_cooldown The number of repeats before the
* cooldown is activated.
* \param cache_flush_bytes The number of bytes to flush from cache before
* \param f_preproc The function to be executed before we execute time
* evaluator.
* \return f_timer A timer function.
*/
ffi::Function WrapTimeEvaluator(ffi::Function f, Device dev, int number, int repeat,
int min_repeat_ms, int limit_zero_time_iterations,
int cooldown_interval_ms, int repeats_to_cooldown,
int cache_flush_bytes = 0, ffi::Function f_preproc = nullptr);
} // namespace runtime
} // namespace tvm
#endif // TVM_RUNTIME_TIMER_H_