blob: 5d5e1b765c9ab798799609e2172701a24dccb014 [file]
//===- GenericProfiler.h - GenericProfiler interface for use in Plugins ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The GenericProfiler interface allows to implement profiler logic for various
// backends, such as OMPT or other tracing mechanisms.
// This enables the plugins to be agnostic of the actual high-level language
// that is implemented.
//
//===----------------------------------------------------------------------===//
#ifndef OFFLOAD_PLUGINS_NEXTGEN_COMMON_INCLUDE_GENERICPROFILER_H
#define OFFLOAD_PLUGINS_NEXTGEN_COMMON_INCLUDE_GENERICPROFILER_H
#include "Shared/APITypes.h"
#include <cstdint>
#include <functional>
#include <tuple>
namespace llvm {
namespace omp {
namespace target {
namespace plugin {
struct GenericDeviceTy;
class GenericProfilerTy;
/// Helper function to unpack a tuple of arguments and call a function with
/// them.
template <typename FunT, typename... ArgsT, size_t... IdxSequence>
void callViaIndexSeq(FunT F, GenericProfilerTy *P, uint64_t StartNanos,
uint64_t EndNanos, std::tuple<ArgsT...> Args,
std::index_sequence<IdxSequence...>) {
F(P, StartNanos, EndNanos, std::get<IdxSequence>(Args)...);
}
/// Helper function to unpack a tuple of arguments and call a function with
/// them.
template <typename FunT, typename... ArgsT>
void callViaUnpack(FunT F, GenericProfilerTy *P, uint64_t StartNanos,
uint64_t EndNanos, std::tuple<ArgsT...> Tup) {
callViaIndexSeq(F, P, StartNanos, EndNanos, Tup,
std::index_sequence_for<ArgsT...>{});
}
/// Abstraction layer to implement different profiler backends.
///
/// The plugins call into the GenericProfilerTy to handle the specific events
/// with whatever specific backend was instantiated. For now, the supported
/// backends are limited to an OMPT implementation.
class GenericProfilerTy {
public:
GenericProfilerTy() = default;
virtual ~GenericProfilerTy() = default;
/// Returns true if profiling is enabled, false otherwise.
virtual bool isProfilingEnabled() { return false; }
/// Set the factors which are used to interpolate the device clock compared to
/// the host clock. This follows a simple linear interpolation: Slope * <time>
/// + Offset.
void setTimeConversionFactors(double Slope, double Offset) {
HostToDeviceSlope = Slope;
HostToDeviceOffset = Offset;
setTimeConversionFactorsImpl(HostToDeviceSlope, HostToDeviceOffset);
}
/// Hook that is called when memory is allocated on the device.
virtual void handleDataAlloc(uint64_t StartNanos, uint64_t EndNanos,
void *HostPtr, uint64_t Size, void *Data) {}
/// Hook that is called when memory is freed on the device.
virtual void handleDataDelete(uint64_t StartNanos, uint64_t EndNanos,
void *TgtPtr, void *Data) {}
/// Hook that is called before launching a kernel.
virtual void handlePreKernelLaunch(GenericDeviceTy *Device,
uint32_t NumBlocks[3],
__tgt_async_info *AI) {}
/// Hook that is called when the kernel is finished to extract the specific
/// timing info for that kernel execution.
virtual void handleKernelCompletion(uint64_t StartNanos, uint64_t EndNanos,
void *Data) {}
/// Hook that is called when a data transfer happens to extract timing info
/// for that transfer.
virtual void handleDataTransfer(uint64_t StartNanos, uint64_t EndNanos,
void *Data) {}
/// Allow factors for time conversion between host and device.
virtual void setTimeConversionFactorsImpl(double Slope, double Offset) {}
/// RAII style timer that measures the elapsed time between construction and
/// destruction, then invokes a callback with the profiler, start/end times,
/// and any captured arguments. It wraps an existing GenericProfiler instance
/// to delay the call of the ProfilerFunction (F) until this object's dtor is
/// called.
template <typename ProfilerFuncTy, typename... ProfilerFuncArgsTy>
class ProfTimerTy {
public:
/// On creation, saves the timestamp from the profiler and device, and
/// stores the callback function and its arguments.
ProfTimerTy(ProfilerFuncTy &&F, GenericProfilerTy *P, GenericDeviceTy *D,
ProfilerFuncArgsTy... As)
: ProfilerFunction(F), ProfilerInstance(P), Device(D),
ProfilerFuncArgs(As...) {
assert(ProfilerInstance && "GenericProfilerTy is null");
assert(Device && "GenericDeviceTy is null");
if (ProfilerInstance)
StartTime = ProfilerInstance->getDeviceTimeStamp(Device);
}
/// On destruction, saves the end timestamp and invokes the callback
/// function with the profiler, start/end times, and captured arguments.
~ProfTimerTy() {
assert(ProfilerInstance && "GenericProfilerTy is null");
assert(Device && "GenericDeviceTy is null");
if (ProfilerInstance) {
uint64_t EndTime = ProfilerInstance->getDeviceTimeStamp(Device);
callViaUnpack(ProfilerFunction, ProfilerInstance, StartTime, EndTime,
ProfilerFuncArgs);
}
}
private:
/// The callback function to be invoked on destruction, which takes the
/// profiler, start/end times, and captured arguments.
ProfilerFuncTy ProfilerFunction;
/// The profiler instance to be used for timing and invoking the callback
/// function.
GenericProfilerTy *ProfilerInstance;
/// The device instance to be used for timing and invoking the callback
/// function.
GenericDeviceTy *Device;
/// The start time of the timer, captured at construction.
uint64_t StartTime = 0;
/// The captured arguments to be passed to the callback function on
/// destruction.
std::tuple<ProfilerFuncArgsTy...> ProfilerFuncArgs;
};
template <typename ProfilerFuncTy, typename... ProfilerFuncArgsTy>
[[maybe_unused]]
ProfTimerTy(ProfilerFuncTy &&, GenericProfilerTy *, ProfilerFuncArgsTy...)
-> ProfTimerTy<ProfilerFuncTy, ProfilerFuncArgsTy...>;
template <typename ProfilerFuncTy, typename... ProfilerFuncArgsTy>
friend class ProfTimerTy;
/// Returns an RAII style timer, which handles data allocation timing.
[[nodiscard]] auto getScopedDataAllocTimer(GenericDeviceTy *Dev,
void *HostPtr, uint64_t Size,
void *ProfData = nullptr) {
return ProfTimerTy(
[](GenericProfilerTy *P, auto... args) {
assert(P && "P was null");
P->handleDataAlloc(args...);
},
this, Dev, HostPtr, Size, ProfData);
}
/// Returns an RAII style timer, which handles data deletion timing.
[[nodiscard]] auto getScopedDataDeleteTimer(GenericDeviceTy *Dev,
void *TgtPtr,
void *ProfData = nullptr) {
return ProfTimerTy(
[](GenericProfilerTy *P, auto... args) {
assert(P && "P was null");
P->handleDataDelete(args...);
},
this, Dev, TgtPtr, ProfData);
}
protected:
/// Factors to convert host time to device time. The default values are
/// Computed in a way Slope * Time + Offset (inspired by Score-P).
double HostToDeviceSlope = 1.0;
double HostToDeviceOffset = .0;
private:
/// Vendor-specific implementation to obtain device time.
uint64_t getDeviceTimeStamp(GenericDeviceTy *D);
};
/// Null-object used to normalize an absent profiler. Callers that do not have a
/// profiler pass none, and the receiving method resolves to this instance, so
/// profiling logic never has to test for a null pointer.
GenericProfilerTy &getNoOpProfiler();
} // namespace plugin
} // namespace target
} // namespace omp
} // namespace llvm
#endif // OFFLOAD_PLUGINS_NEXTGEN_COMMON_INCLUDE_GENERICPROFILER_H