chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
proto_library(phi_profiler_proto SRCS profiler.proto)
if(WITH_PYTHON AND EXISTS ${PADDLE_BINARY_DIR})
set(FLUID_PATH ${PADDLE_BINARY_DIR}/python/paddle/base)
py_proto_compile(profiler_py_proto SRCS profiler.proto)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/__init__.py)
if(NOT WIN32)
add_custom_command(
TARGET profiler_py_proto
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${FLUID_PATH}/proto/profiler
COMMAND cp *.py ${FLUID_PATH}/proto/profiler
COMMENT
"Copy generated python proto into directory paddle/fluid/proto/profiler."
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
else()
string(REPLACE "/" "\\" proto_dstpath "${FLUID_PATH}/proto/profiler/")
add_custom_command(
TARGET profiler_py_proto
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${FLUID_PATH}/proto/profiler
COMMAND copy /Y *.py ${proto_dstpath}
COMMENT
"Copy generated python proto into directory paddle/fluid/proto/profiler."
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()
endif()
collect_srcs(api_srcs SRCS device_tracer.cc event.cc profiler.cc)
+135
View File
@@ -0,0 +1,135 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed 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.
#pragma once
#include <cstring>
#include <functional>
#include <string>
#include "paddle/common/ddim.h"
#include "paddle/phi/api/profiler/event.h" // import EventRole, TODO(TIEXING): remove later
#include "paddle/phi/api/profiler/trace_event.h"
#include "paddle/phi/core/attribute.h"
namespace phi {
struct CommonEvent {
public:
CommonEvent(const char *name,
uint64_t start_ns,
uint64_t end_ns,
EventRole role,
TracerEventType type)
: name(name),
start_ns(start_ns),
end_ns(end_ns),
role(role),
type(type) {}
CommonEvent(std::function<void *(size_t)> arena_allocator,
const std::string &name_str,
uint64_t start_ns,
uint64_t end_ns,
EventRole role,
TracerEventType type,
const std::string &attr_str)
: start_ns(start_ns), end_ns(end_ns), role(role), type(type) {
auto buf = static_cast<char *>(arena_allocator(name_str.length() + 1));
strncpy(buf, name_str.c_str(), name_str.length() + 1);
name = buf;
buf = static_cast<char *>(arena_allocator(attr_str.length() + 1));
strncpy(buf, attr_str.c_str(), attr_str.length() + 1);
attr = buf;
}
CommonEvent(std::function<void *(size_t)> arena_allocator,
const std::string &name_str,
uint64_t start_ns,
uint64_t end_ns,
EventRole role,
TracerEventType type)
: start_ns(start_ns), end_ns(end_ns), role(role), type(type) {
auto buf = static_cast<char *>(arena_allocator(name_str.length() + 1));
strncpy(buf, name_str.c_str(), name_str.length() + 1);
name = buf;
}
const char *name = nullptr; // not owned, designed for performance
uint64_t start_ns = 0;
uint64_t end_ns = 0;
EventRole role = EventRole::kOrdinary;
TracerEventType type = TracerEventType::NumTypes;
const char *attr = nullptr; // not owned, designed for performance
};
struct CommonMemEvent {
public:
CommonMemEvent(uint64_t timestamp_ns,
uint64_t addr,
TracerMemEventType type,
int64_t increase_bytes,
const Place &place,
uint64_t current_allocated,
uint64_t current_reserved,
uint64_t peak_allocated,
uint64_t peak_reserved)
: timestamp_ns(timestamp_ns),
addr(addr),
type(type),
increase_bytes(increase_bytes),
place(place),
current_allocated(current_allocated),
current_reserved(current_reserved),
peak_allocated(peak_allocated),
peak_reserved(peak_reserved) {}
uint64_t timestamp_ns;
uint64_t addr;
TracerMemEventType type;
int64_t increase_bytes;
Place place;
uint64_t current_allocated;
uint64_t current_reserved;
uint64_t peak_allocated;
uint64_t peak_reserved;
};
struct OperatorSupplementOriginEvent {
public:
OperatorSupplementOriginEvent(
std::function<void *(size_t)> arena_allocator,
uint64_t timestamp_ns,
const std::string &type_name,
const std::vector<std::pair<const char *, std::vector<DDim>>> &shapes,
const AttributeMap &attributes,
uint64_t op_id)
: timestamp_ns(timestamp_ns), attributes(attributes), op_id(op_id) {
auto buf = static_cast<char *>(arena_allocator(type_name.length() + 1));
strncpy(buf, type_name.c_str(), type_name.length() + 1);
op_type = buf;
for (auto it = shapes.begin(); it != shapes.end(); it++) {
input_shapes[std::string((*it).first)] = (*it).second;
}
}
uint64_t timestamp_ns;
const char *op_type = nullptr; // not owned, designed for performance
// input shapes
std::map<std::string, std::vector<DDim>> input_shapes;
// op attributes
AttributeMap attributes;
// op id
uint64_t op_id;
};
} // namespace phi
+913
View File
@@ -0,0 +1,913 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#include "paddle/phi/api/profiler/device_tracer.h"
#include <deque>
#include <forward_list>
#include <fstream>
#include <mutex> // NOLINT
#include <string>
#include <thread> // NOLINT
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/core/enforce.h"
PHI_DECLARE_bool(enable_host_event_recorder_hook);
namespace phi {
// Used only by DeviceTracer
uint64_t GetThreadIdFromSystemThreadId(uint32_t id);
namespace {
// Tracking the nested block stacks of each thread.
#ifdef PADDLE_WITH_SW
// sw not supported thread_local
std::deque<int> block_id_stack;
std::deque<Event *> annotation_stack;
#else
// Tracking the nested event stacks.
thread_local std::deque<int> block_id_stack;
// Tracking the nested event stacks.
thread_local std::deque<Event *> annotation_stack;
#endif
// stack to store event such as pe and so on
static std::deque<Event *> main_thread_annotation_stack{};
static std::deque<std::string> main_thread_annotation_stack_name{};
std::map<uint32_t, uint64_t> system_thread_id_map;
std::mutex system_thread_id_map_mutex;
std::once_flag tracer_once_flag;
DeviceTracer *tracer = nullptr;
void PrintCuptiHint() {
static bool showed = false;
if (showed) return;
showed = true;
LOG(WARNING) << "Invalid timestamp occurred. Please try increasing the "
"FLAGS_multiple_of_cupti_buffer_size.";
}
} // namespace
#ifdef PADDLE_WITH_CUPTI
namespace {
// The experimental best performance is
// the same size with CUPTI device buffer size(8M)
uint64_t kBufSize = 1024 * 1024 * 8;
uint64_t kAlignSize = 8;
std::unordered_map<CUpti_CallbackId, std::string> runtime_cbid_str,
driver_cbid_str;
#define ALIGN_BUFFER(buffer, align) \
(((uintptr_t)(buffer) & ((align)-1)) \
? ((buffer) + (align) - ((uintptr_t)(buffer) & ((align)-1))) \
: (buffer))
#define CUPTI_CALL(call) \
do { \
CUptiResult _status = call; \
if (_status != CUPTI_SUCCESS) { \
const char *errstr; \
dynload::cuptiGetResultString(_status, &errstr); \
fprintf(stderr, \
"%s:%d: error: function %s failed with error %s.\n", \
__FILE__, \
__LINE__, \
#call, \
errstr); \
exit(-1); \
} \
} while (0)
std::string MemcpyKind(CUpti_ActivityMemcpyKind kind) {
switch (kind) {
case CUPTI_ACTIVITY_MEMCPY_KIND_HTOD:
return "MEMCPY_HtoD";
case CUPTI_ACTIVITY_MEMCPY_KIND_DTOH:
return "MEMCPY_DtoH";
case CUPTI_ACTIVITY_MEMCPY_KIND_HTOA:
return "MEMCPY_HtoA";
case CUPTI_ACTIVITY_MEMCPY_KIND_ATOH:
return "MEMCPY_AtoH";
case CUPTI_ACTIVITY_MEMCPY_KIND_ATOA:
return "MEMCPY_AtoA";
case CUPTI_ACTIVITY_MEMCPY_KIND_ATOD:
return "MEMCPY_AtoD";
case CUPTI_ACTIVITY_MEMCPY_KIND_DTOA:
return "MEMCPY_DtoA";
case CUPTI_ACTIVITY_MEMCPY_KIND_DTOD:
return "MEMCPY_DtoD";
case CUPTI_ACTIVITY_MEMCPY_KIND_HTOH:
return "MEMCPY_HtoH";
case CUPTI_ACTIVITY_MEMCPY_KIND_PTOP:
return "MEMCPY_PtoP";
case CUPTI_ACTIVITY_MEMCPY_KIND_FORCE_INT:
return "MEMCPY_FORCE_INT";
default:
break;
}
return "MEMCPY";
}
std::string DriverKind(CUpti_CallbackId cbid) {
auto iter = driver_cbid_str.find(cbid);
if (iter == driver_cbid_str.end())
return "Driver API " + std::to_string(cbid);
return iter->second;
}
std::string RuntimeKind(CUpti_CallbackId cbid) {
auto iter = runtime_cbid_str.find(cbid);
if (iter == runtime_cbid_str.end())
return "Runtime API " + std::to_string(cbid);
return iter->second;
}
void EnableActivity() {
// Device activity record is created when CUDA initializes, so we
// want to enable it before cuInit() or any CUDA runtime call.
CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_MEMCPY));
CUPTI_CALL(
dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_CONCURRENT_KERNEL));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_KERNEL));
CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_DRIVER));
CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_RUNTIME));
// We don't track these activities for now.
CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_MEMSET));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_OVERHEAD));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_DEVICE));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_CONTEXT));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_DRIVER));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_RUNTIME));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_NAME));
// CUPTI_CALL(dynload::cuptiActivityEnable(CUPTI_ACTIVITY_KIND_MARKER));
}
void DisableActivity() {
CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_MEMCPY));
CUPTI_CALL(
dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_CONCURRENT_KERNEL));
// CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_DEVICE));
// Disable all other activity record kinds.
// CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_CONTEXT));
CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_DRIVER));
CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_RUNTIME));
CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_MEMSET));
// CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_NAME));
// CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_MARKER));
// CUPTI_CALL(dynload::cuptiActivityDisable(CUPTI_ACTIVITY_KIND_OVERHEAD));
}
void CUPTIAPI bufferRequested(uint8_t **buffer,
size_t *size,
size_t *maxNumRecords) {
uint8_t *buf = reinterpret_cast<uint8_t *>(malloc(kBufSize + kAlignSize));
*size = kBufSize;
*buffer = ALIGN_BUFFER(buf, kAlignSize);
*maxNumRecords = 0;
}
void CUPTIAPI bufferCompleted(CUcontext ctx,
uint32_t streamId,
uint8_t *buffer,
size_t size,
size_t validSize) {
static std::thread::id cupti_thread_id(0);
if (cupti_thread_id == std::thread::id(0))
cupti_thread_id = std::this_thread::get_id();
PADDLE_ENFORCE_EQ(
std::this_thread::get_id(),
cupti_thread_id,
errors::PermissionDenied(
"Only one thread is allowed to call bufferCompleted()."));
CUptiResult status;
CUpti_Activity *record = nullptr;
if (validSize > 0) {
do {
status = dynload::cuptiActivityGetNextRecord(buffer, validSize, &record);
if (status == CUPTI_SUCCESS) {
switch (record->kind) {
case CUPTI_ACTIVITY_KIND_KERNEL:
case CUPTI_ACTIVITY_KIND_CONCURRENT_KERNEL: {
#if CUDA_VERSION >= 9000 || defined(PADDLE_WITH_XPU)
auto *kernel =
reinterpret_cast<const CUpti_ActivityKernel4 *>(record);
#else
auto *kernel =
reinterpret_cast<const CUpti_ActivityKernel3 *>(record);
#endif
tracer->AddKernelRecords(kernel->name,
kernel->start,
kernel->end,
kernel->deviceId,
kernel->streamId,
kernel->correlationId);
break;
}
case CUPTI_ACTIVITY_KIND_MEMCPY: {
auto *memcpy =
reinterpret_cast<const CUpti_ActivityMemcpy *>(record);
tracer->AddMemRecords(
MemcpyKind(
static_cast<CUpti_ActivityMemcpyKind>(memcpy->copyKind)),
memcpy->start,
memcpy->end,
memcpy->deviceId,
memcpy->streamId,
memcpy->correlationId,
memcpy->bytes);
break;
}
case CUPTI_ACTIVITY_KIND_MEMCPY2: {
auto *memcpy =
reinterpret_cast<const CUpti_ActivityMemcpy2 *>(record);
tracer->AddMemRecords(
MemcpyKind(
static_cast<CUpti_ActivityMemcpyKind>(memcpy->copyKind)),
memcpy->start,
memcpy->end,
memcpy->deviceId,
memcpy->streamId,
memcpy->correlationId,
memcpy->bytes);
break;
}
case CUPTI_ACTIVITY_KIND_MEMSET: {
auto *memset =
reinterpret_cast<const CUpti_ActivityMemset *>(record);
tracer->AddKernelRecords("MEMSET",
memset->start,
memset->end,
memset->deviceId,
memset->streamId,
memset->correlationId);
break;
}
case CUPTI_ACTIVITY_KIND_DRIVER: {
auto *api = reinterpret_cast<const CUpti_ActivityAPI *>(record);
if (api->start != 0 && api->end != 0) {
// -1 device id represents ActiveKind api call
tracer->AddActiveKindRecords(
DriverKind(api->cbid),
api->start,
api->end,
-1,
GetThreadIdFromSystemThreadId(api->threadId),
api->correlationId);
}
break;
}
case CUPTI_ACTIVITY_KIND_RUNTIME: {
auto *api = reinterpret_cast<const CUpti_ActivityAPI *>(record);
if (api->start != 0 && api->end != 0) {
// -1 device id represents ActiveKind api call
tracer->AddActiveKindRecords(
RuntimeKind(api->cbid),
api->start,
api->end,
-1,
GetThreadIdFromSystemThreadId(api->threadId),
api->correlationId);
}
break;
}
default: {
break;
}
}
} else if (status == CUPTI_ERROR_MAX_LIMIT_REACHED) {
// Seems not an error in this case.
break;
} else {
CUPTI_CALL(status);
}
} while (true);
size_t dropped;
CUPTI_CALL(
dynload::cuptiActivityGetNumDroppedRecords(ctx, streamId, &dropped));
if (dropped != 0) {
fprintf(stderr, "Dropped %u activity records\n", (unsigned int)dropped);
PrintCuptiHint();
}
}
free(buffer);
}
void initCuptiCbidStr();
} // namespace
#endif // PADDLE_WITH_CUPTI
class DeviceTracerImpl : public DeviceTracer {
public:
DeviceTracerImpl() : enabled_(false), start_ns_(0), end_ns_(0) {
#ifdef PADDLE_WITH_CUPTI
initCuptiCbidStr();
#endif
}
void AddAnnotation(uint32_t id, Event *event) override {
#ifdef PADDLE_WITH_SW
std::forward_list<std::pair<uint32_t, Event *>> *local_correlations_pairs =
nullptr;
#else
thread_local std::forward_list<std::pair<uint32_t, Event *>>
*local_correlations_pairs = nullptr;
#endif
if (local_correlations_pairs == nullptr) {
std::lock_guard<std::mutex> l(trace_mu_);
correlations_pairs.emplace_front();
local_correlations_pairs = &correlations_pairs.front();
}
local_correlations_pairs->push_front(std::make_pair(id, event));
}
void AddAnnotations(
const std::map<uint64_t, ThreadEvents> &thr_events) override {
for (auto &tmp : active_kind_records_) {
for (const ActiveKindRecord &r : tmp) {
auto iter = thr_events.find(r.thread_id);
if (iter == thr_events.end()) {
VLOG(10) << __func__ << " " << r.name
<< " Missing tid: " << r.thread_id;
continue;
}
const ThreadEvents &evts = iter->second;
auto evt_iter = evts.upper_bound(r.end_ns);
if (evt_iter == evts.end()) {
VLOG(10) << __func__ << " Missing Record " << r.name
<< " tid: " << r.thread_id << " end_ns: " << r.end_ns;
continue;
}
if (evt_iter != evts.begin()) {
auto prev_iter = std::prev(evt_iter);
if (prev_iter->first >= r.end_ns) {
evt_iter = prev_iter;
} else {
VLOG(10) << __func__ << " prev end_ns " << prev_iter->first
<< " end_ns: " << r.end_ns;
}
}
Event *evt = evt_iter->second.first;
uint64_t start_ns = evt_iter->second.second;
if (start_ns > r.start_ns) {
VLOG(10) << __func__ << " Mismatch Record " << r.name
<< " tid: " << r.thread_id << " start_ns: " << r.start_ns
<< " end_ns: " << r.end_ns << ", event " << evt->name()
<< " start_ns: " << start_ns;
continue;
}
VLOG(10) << __func__ << " tid: " << r.thread_id << " Add correlation "
<< r.correlation_id << "<->" << evt->name();
AddAnnotation(r.correlation_id, evt);
}
}
}
void AddCPURecords(const std::string &anno,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
uint64_t thread_id) override {
if (anno.empty()) {
VLOG(1) << "Empty timeline annotation.";
return;
}
#ifdef PADDLE_WITH_SW
std::forward_list<CPURecord> *local_cpu_records_ = nullptr;
#else
thread_local std::forward_list<CPURecord> *local_cpu_records_ = nullptr;
#endif
if (local_cpu_records_ == nullptr) {
std::lock_guard<std::mutex> l(trace_mu_);
cpu_records_.emplace_front();
local_cpu_records_ = &cpu_records_.front();
}
local_cpu_records_->push_front(
CPURecord{anno, start_ns, end_ns, device_id, thread_id});
}
void AddMemRecords(const std::string &name,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
int64_t stream_id,
uint32_t correlation_id,
uint64_t bytes) override {
// 0 means timestamp information could not be collected for the kernel.
if (start_ns == 0 || end_ns == 0 || start_ns == end_ns) {
VLOG(3) << name << " cannot be traced";
PrintCuptiHint();
return;
}
// NOTE(liangdun): lock is not needed, only one thread call this function.
mem_records_.push_front(MemRecord{
name, start_ns, end_ns, device_id, stream_id, correlation_id, bytes});
}
void AddMemInfoRecord(uint64_t start_ns,
uint64_t end_ns,
size_t bytes,
const Place &place,
const std::string &alloc_in,
const std::string &free_in,
uint64_t thread_id) override {
if (0 == start_ns || 0 == end_ns) {
VLOG(3) << alloc_in << ", " << free_in << " Cannot be traced.";
return;
}
#ifdef PADDLE_WITH_SW
std::forward_list<MemInfoRecord> *local_mem_info_record = nullptr;
#else
thread_local std::forward_list<MemInfoRecord> *local_mem_info_record =
nullptr;
#endif
if (local_mem_info_record == nullptr) {
std::lock_guard<std::mutex> l(trace_mu_);
mem_info_record_.emplace_front();
local_mem_info_record = &mem_info_record_.front();
}
local_mem_info_record->emplace_front(MemInfoRecord{
start_ns, end_ns, bytes, place, thread_id, alloc_in, free_in});
}
void AddActiveKindRecords(const std::string &anno,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
uint64_t thread_id,
uint32_t correlation_id) override {
if (anno.empty()) {
VLOG(1) << "Empty timeline annotation.";
return;
}
#ifdef PADDLE_WITH_SW
std::forward_list<ActiveKindRecord> *local_active_kind_records = nullptr;
#else
thread_local std::forward_list<ActiveKindRecord>
*local_active_kind_records = nullptr;
#endif
if (local_active_kind_records == nullptr) {
std::lock_guard<std::mutex> l(trace_mu_);
active_kind_records_.emplace_front();
local_active_kind_records = &active_kind_records_.front();
}
// lock is not needed, only one thread call this function.
local_active_kind_records->push_front(ActiveKindRecord{
anno, start_ns, end_ns, device_id, thread_id, correlation_id});
}
void AddKernelRecords(std::string name,
uint64_t start,
uint64_t end,
int64_t device_id,
int64_t stream_id,
uint32_t correlation_id) override {
// 0 means timestamp information could not be collected for the kernel.
if (start == 0 || end == 0 || start == end) {
VLOG(3) << correlation_id << " cannot be traced";
PrintCuptiHint();
return;
}
// NOTE(liangdun): lock is not needed, only one thread call this function.
kernel_records_.push_front(
KernelRecord{name, start, end, device_id, stream_id, correlation_id});
}
bool IsEnabled() override {
std::lock_guard<std::mutex> l(trace_mu_);
return enabled_;
}
void Enable() override {
std::lock_guard<std::mutex> l(trace_mu_);
if (enabled_) {
return;
}
#ifdef PADDLE_WITH_CUPTI
EnableActivity();
// Register callbacks for buffer requests and completed by CUPTI.
CUPTI_CALL(dynload::cuptiActivityRegisterCallbacks(bufferRequested,
bufferCompleted));
CUptiResult ret;
ret = dynload::cuptiSubscribe(
&subscriber_, static_cast<CUpti_CallbackFunc>(ApiCallback), this);
if (ret == CUPTI_ERROR_MAX_LIMIT_REACHED) {
fprintf(stderr, "CUPTI subscriber limit reached.\n");
} else if (ret != CUPTI_SUCCESS) {
fprintf(stderr, "Failed to create CUPTI subscriber.\n");
}
const std::vector<int> runtime_cbids {
CUPTI_RUNTIME_TRACE_CBID_cudaMemcpy_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaSetupArgument_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaMemcpyAsync_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaMemset_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaMemsetAsync_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaLaunch_v3020,
CUPTI_RUNTIME_TRACE_CBID_cudaLaunchKernel_v7000
#if CUDA_VERSION >= 9000 || defined(PADDLE_WITH_XPU)
,
CUPTI_RUNTIME_TRACE_CBID_cudaLaunchCooperativeKernel_v9000,
CUPTI_RUNTIME_TRACE_CBID_cudaLaunchCooperativeKernelMultiDevice_v9000
#endif
};
const std::vector<int> driver_cbids{CUPTI_DRIVER_TRACE_CBID_cuLaunch,
CUPTI_DRIVER_TRACE_CBID_cuLaunchGrid,
CUPTI_DRIVER_TRACE_CBID_cuLaunchKernel};
for (auto cbid : runtime_cbids)
CUPTI_CALL(dynload::cuptiEnableCallback(
1, subscriber_, CUPTI_CB_DOMAIN_RUNTIME_API, cbid));
for (auto cbid : driver_cbids)
CUPTI_CALL(dynload::cuptiEnableCallback(
1, subscriber_, CUPTI_CB_DOMAIN_DRIVER_API, cbid));
CUPTI_CALL(dynload::cuptiGetTimestamp(&start_ns_));
#endif // PADDLE_WITH_CUPTI
enabled_ = true;
}
void Reset() override {
#ifdef PADDLE_WITH_CUPTI
CUPTI_CALL(
dynload::cuptiActivityFlushAll(CUPTI_ACTIVITY_FLAG_FLUSH_FORCED));
#endif
std::lock_guard<std::mutex> l(trace_mu_);
kernel_records_.clear();
mem_records_.clear();
correlations_.clear();
for (auto &tmp : correlations_pairs) tmp.clear();
for (auto &tmp : cpu_records_) tmp.clear();
for (auto &tmp : mem_info_record_) tmp.clear();
for (auto &tmp : active_kind_records_) tmp.clear();
}
void GenEventKernelCudaElapsedTime() override {
#ifdef PADDLE_WITH_CUPTI
if (correlations_.empty())
for (auto &tmp : correlations_pairs)
for (auto &pair : tmp) correlations_[pair.first] = pair.second;
for (const KernelRecord &r : kernel_records_) {
auto c = correlations_.find(r.correlation_id);
if (c != correlations_.end() && c->second != nullptr) {
Event *e = c->second;
Event *parent = e->parent();
while (parent) {
parent->AddCudaElapsedTime(r.start_ns, r.end_ns); // NOLINT
parent = parent->parent();
}
e->AddCudaElapsedTime(r.start_ns, r.end_ns); // NOLINT
}
}
for (const auto &r : mem_records_) {
auto c = correlations_.find(r.correlation_id);
if (c != correlations_.end() && c->second != nullptr) {
Event *e = c->second;
Event *parent = e->parent();
while (parent) {
parent->AddCudaElapsedTime(r.start_ns, r.end_ns); // NOLINT
parent = parent->parent();
}
e->AddCudaElapsedTime(r.start_ns, r.end_ns); // NOLINT
}
}
#endif
}
proto::Profile GenProfile(const std::string &profile_path) override {
proto::Profile profile_pb = this->GetProfile();
std::ofstream profile_f;
profile_f.open(profile_path,
std::ios::out | std::ios::trunc | std::ios::binary);
profile_pb.SerializeToOstream(&profile_f);
profile_f.close();
return profile_pb;
}
proto::Profile GetProfile() override {
int miss = 0, find = 0;
std::lock_guard<std::mutex> l(trace_mu_);
proto::Profile profile_pb;
profile_pb.set_start_ns(start_ns_);
profile_pb.set_end_ns(end_ns_);
if (correlations_.empty()) {
for (auto &tmp : correlations_pairs) {
for (auto &pair : tmp) correlations_[pair.first] = pair.second;
}
}
for (const KernelRecord &r : kernel_records_) {
auto *event = profile_pb.add_events();
event->set_type(proto::Event::GPUKernel);
auto c = correlations_.find(r.correlation_id);
if (c != correlations_.end() && c->second != nullptr) {
event->set_name(c->second->name());
event->set_detail_info(c->second->attr());
find++;
} else {
VLOG(10) << __func__ << " Missing Kernel Event: " + r.name;
miss++;
event->set_name(r.name);
}
event->set_start_ns(r.start_ns);
event->set_end_ns(r.end_ns);
event->set_sub_device_id(r.stream_id);
event->set_device_id(r.device_id);
}
VLOG(1) << __func__ << " KernelRecord event miss: " << miss
<< " find: " << find;
for (auto &tmp : cpu_records_) {
for (const CPURecord &r : tmp) {
auto *event = profile_pb.add_events();
event->set_type(proto::Event::CPU);
event->set_name(r.name);
event->set_start_ns(r.start_ns);
event->set_end_ns(r.end_ns);
event->set_sub_device_id(r.thread_id);
event->set_device_id(r.device_id);
}
}
for (auto &tmp : active_kind_records_) {
for (const ActiveKindRecord &r : tmp) {
auto *event = profile_pb.add_events();
event->set_type(proto::Event::CPU);
auto c = correlations_.find(r.correlation_id);
if (c != correlations_.end() && c->second != nullptr) {
event->set_name(c->second->name());
event->set_detail_info(r.name);
} else {
event->set_name(r.name);
}
event->set_start_ns(r.start_ns);
event->set_end_ns(r.end_ns);
event->set_sub_device_id(r.thread_id);
event->set_device_id(r.device_id);
}
}
miss = find = 0;
for (const MemRecord &r : mem_records_) {
auto *event = profile_pb.add_events();
event->set_type(proto::Event::GPUKernel);
auto c = correlations_.find(r.correlation_id);
if (c != correlations_.end() && c->second != nullptr) {
event->set_name(c->second->name());
event->set_detail_info(r.name);
find++;
} else {
miss++;
event->set_name(r.name);
}
event->set_start_ns(r.start_ns);
event->set_end_ns(r.end_ns);
event->set_sub_device_id(r.stream_id);
event->set_device_id(r.device_id);
event->mutable_memcopy()->set_bytes(r.bytes);
}
VLOG(1) << __func__ << " MemRecord event miss: " << miss
<< " find: " << find;
for (auto &tmp : mem_info_record_) {
for (const auto &r : tmp) {
auto *event = profile_pb.add_mem_events();
event->set_device_id(0);
if (r.place.GetType() == phi::AllocationType::CPU) {
event->set_place(proto::MemEvent::CPUPlace);
} else if (r.place.GetType() == phi::AllocationType::GPU) {
event->set_place(proto::MemEvent::CUDAPlace);
event->set_device_id(r.place.GetDeviceId());
} else if (r.place.GetType() == phi::AllocationType::GPUPINNED) {
event->set_place(proto::MemEvent::CUDAPinnedPlace);
} else {
PADDLE_THROW(
errors::Unimplemented("The current place is not supported."));
}
event->set_alloc_in(r.alloc_in);
event->set_free_in(r.free_in);
event->set_start_ns(r.start_ns);
event->set_end_ns(r.end_ns);
event->set_bytes(r.bytes);
event->set_thread_id(r.thread_id);
}
}
return profile_pb;
}
void Disable() override {
#ifdef PADDLE_WITH_CUPTI
// flush might cause additional calls to DeviceTracker.
CUPTI_CALL(
dynload::cuptiActivityFlushAll(CUPTI_ACTIVITY_FLAG_FLUSH_FORCED));
#endif // PADDLE_WITH_CUPTI
std::lock_guard<std::mutex> l(trace_mu_);
#ifdef PADDLE_WITH_CUPTI
DisableActivity();
CUPTI_CALL(dynload::cuptiUnsubscribe(subscriber_));
CUPTI_CALL(dynload::cuptiGetTimestamp(&end_ns_));
#endif // PADDLE_WITH_CUPTI
enabled_ = false;
}
private:
#ifdef PADDLE_WITH_CUPTI
static void CUPTIAPI ApiCallback(void *userdata,
CUpti_CallbackDomain domain,
CUpti_CallbackId cbid,
const void *cbdata) {
if (LIKELY(FLAGS_enable_host_event_recorder_hook)) {
return;
}
auto *cbInfo = reinterpret_cast<const CUpti_CallbackData *>(cbdata);
DeviceTracerImpl *tracer = reinterpret_cast<DeviceTracerImpl *>(userdata);
if (cbInfo->callbackSite == CUPTI_API_ENTER) {
Event *event = CurAnnotation();
tracer->AddAnnotation(cbInfo->correlationId, event);
}
}
CUpti_SubscriberHandle subscriber_;
#endif // PADDLE_WITH_CUPTI
std::mutex trace_mu_;
bool enabled_;
uint64_t start_ns_;
uint64_t end_ns_;
std::forward_list<KernelRecord> kernel_records_;
std::forward_list<MemRecord> mem_records_;
std::forward_list<std::forward_list<CPURecord>> cpu_records_;
std::forward_list<std::forward_list<MemInfoRecord>> mem_info_record_;
std::forward_list<std::forward_list<ActiveKindRecord>> active_kind_records_;
std::forward_list<std::forward_list<std::pair<uint32_t, Event *>>>
correlations_pairs;
std::unordered_map<uint32_t, Event *> correlations_;
};
void CreateTracer(DeviceTracer **t) { *t = new DeviceTracerImpl(); }
DeviceTracer *GetDeviceTracer() {
std::call_once(tracer_once_flag, CreateTracer, &tracer);
return tracer;
}
// In order to record PE time, we add main_thread_annotation_stack
// for all event between PE run, we treat it as PE's child Event,
// so when event is not in same thread of PE event, we need add
// father event(PE::run event) for this event
void SetCurAnnotation(Event *event) {
if (!annotation_stack.empty()) {
event->set_parent(annotation_stack.back());
event->set_name(annotation_stack.back()->name() + "/" + event->name());
}
if (annotation_stack.empty() && !main_thread_annotation_stack.empty() &&
main_thread_annotation_stack.back()->thread_id() != event->thread_id()) {
event->set_parent(main_thread_annotation_stack.back());
event->set_name(main_thread_annotation_stack.back()->name() + "/" +
event->name());
}
annotation_stack.push_back(event);
if (event->role() == EventRole::kSpecial) {
std::string name = event->name();
if (!main_thread_annotation_stack_name.empty()) {
name = main_thread_annotation_stack_name.back() + "/" + event->name();
}
main_thread_annotation_stack_name.push_back(name);
main_thread_annotation_stack.push_back(event);
}
}
void ClearCurAnnotation() {
if (!main_thread_annotation_stack.empty()) {
std::string name = annotation_stack.back()->name();
std::string main_name = main_thread_annotation_stack.back()->name();
int main_name_len = static_cast<int>(main_name.length());
int name_len = static_cast<int>(name.length());
int prefix_len = main_name_len - name_len;
if ((prefix_len > 0 && main_name.at(prefix_len - 1) == '/' &&
name == main_name.substr(prefix_len, name_len)) ||
(name == main_name)) {
main_thread_annotation_stack_name.pop_back();
main_thread_annotation_stack.pop_back();
}
}
annotation_stack.pop_back();
}
Event *CurAnnotation() {
if (annotation_stack.empty()) return nullptr;
return annotation_stack.back();
}
std::string CurAnnotationName() {
if (annotation_stack.empty()) return "Unknown";
return annotation_stack.back()->name();
}
void SetCurBlock(int block_id) { block_id_stack.push_back(block_id); }
void ClearCurBlock() { block_id_stack.pop_back(); }
int BlockDepth() { return static_cast<int>(block_id_stack.size()); }
uint32_t GetCurSystemThreadId() {
std::stringstream ss;
ss << std::this_thread::get_id();
uint32_t id = static_cast<uint32_t>(std::stoull(ss.str()));
return id;
}
void RecordCurThreadId(uint64_t id) {
std::lock_guard<std::mutex> lock(system_thread_id_map_mutex);
auto gid = GetCurSystemThreadId();
system_thread_id_map[gid] = id;
}
uint64_t GetThreadIdFromSystemThreadId(uint32_t id) {
auto it = system_thread_id_map.find(id);
if (it != system_thread_id_map.end()) return it->second;
// return origin id if no event is recorded in this thread.
return static_cast<int32_t>(id);
}
#ifdef PADDLE_WITH_CUPTI
namespace {
void initCuptiCbidStr() {
static bool called = false;
if (called) return;
called = true;
#define REGISTER_RUNTIME_CBID_STR(cbid) \
runtime_cbid_str[CUPTI_RUNTIME_TRACE_CBID_##cbid] = #cbid
REGISTER_RUNTIME_CBID_STR(cudaBindTexture_v3020);
REGISTER_RUNTIME_CBID_STR(cudaConfigureCall_v3020);
REGISTER_RUNTIME_CBID_STR(cudaDeviceGetAttribute_v5000);
REGISTER_RUNTIME_CBID_STR(cudaDeviceGetStreamPriorityRange_v5050);
REGISTER_RUNTIME_CBID_STR(cudaDeviceSynchronize_v3020);
REGISTER_RUNTIME_CBID_STR(cudaDriverGetVersion_v3020);
REGISTER_RUNTIME_CBID_STR(cudaEventCreateWithFlags_v3020);
REGISTER_RUNTIME_CBID_STR(cudaEventDestroy_v3020);
REGISTER_RUNTIME_CBID_STR(cudaEventDestroy_v3020);
REGISTER_RUNTIME_CBID_STR(cudaEventQuery_v3020);
REGISTER_RUNTIME_CBID_STR(cudaEventRecord_v3020);
REGISTER_RUNTIME_CBID_STR(cudaFreeHost_v3020);
REGISTER_RUNTIME_CBID_STR(cudaFree_v3020);
REGISTER_RUNTIME_CBID_STR(cudaFuncGetAttributes_v3020);
REGISTER_RUNTIME_CBID_STR(cudaGetDeviceCount_v3020);
REGISTER_RUNTIME_CBID_STR(cudaGetDeviceProperties_v3020);
REGISTER_RUNTIME_CBID_STR(cudaGetDevice_v3020);
REGISTER_RUNTIME_CBID_STR(cudaGetErrorString_v3020);
REGISTER_RUNTIME_CBID_STR(cudaGetLastError_v3020);
REGISTER_RUNTIME_CBID_STR(cudaHostAlloc_v3020);
REGISTER_RUNTIME_CBID_STR(cudaHostGetDevicePointer_v3020);
REGISTER_RUNTIME_CBID_STR(cudaLaunchKernel_v7000);
REGISTER_RUNTIME_CBID_STR(cudaMallocHost_v3020);
REGISTER_RUNTIME_CBID_STR(cudaMalloc_v3020);
REGISTER_RUNTIME_CBID_STR(cudaMemcpyAsync_v3020);
REGISTER_RUNTIME_CBID_STR(cudaMemcpy_v3020);
REGISTER_RUNTIME_CBID_STR(cudaMemsetAsync_v3020);
REGISTER_RUNTIME_CBID_STR(cudaMemset_v3020);
REGISTER_RUNTIME_CBID_STR(
cudaOccupancyMaxActiveBlocksPerMultiprocessorWithFlags_v7000);
REGISTER_RUNTIME_CBID_STR(cudaPeekAtLastError_v3020);
REGISTER_RUNTIME_CBID_STR(cudaRuntimeGetVersion_v3020);
REGISTER_RUNTIME_CBID_STR(cudaSetDevice_v3020);
REGISTER_RUNTIME_CBID_STR(cudaStreamCreate_v3020);
REGISTER_RUNTIME_CBID_STR(cudaStreamCreateWithFlags_v5000);
REGISTER_RUNTIME_CBID_STR(cudaStreamCreateWithPriority_v5050);
REGISTER_RUNTIME_CBID_STR(cudaStreamDestroy_v5050);
REGISTER_RUNTIME_CBID_STR(cudaStreamSynchronize_v3020);
REGISTER_RUNTIME_CBID_STR(cudaStreamWaitEvent_v3020);
REGISTER_RUNTIME_CBID_STR(cudaUnbindTexture_v3020);
REGISTER_RUNTIME_CBID_STR(cudaSetupArgument_v3020);
REGISTER_RUNTIME_CBID_STR(cudaLaunch_v3020);
REGISTER_RUNTIME_CBID_STR(cudaDeviceGetPCIBusId_v4010);
#if CUDA_VERSION >= 9000 || defined(PADDLE_WITH_XPU)
REGISTER_RUNTIME_CBID_STR(cudaLaunchCooperativeKernel_v9000);
REGISTER_RUNTIME_CBID_STR(cudaLaunchCooperativeKernelMultiDevice_v9000);
#endif
#undef REGISTER_RUNTIME_CBID_STR
}
} // namespace
#endif // PADDLE_WITH_CUPTI
} // namespace phi
+166
View File
@@ -0,0 +1,166 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <chrono> // NOLINT
#include <string>
#include "paddle/phi/api/profiler/event.h"
#include "paddle/phi/api/profiler/profiler.pb.h"
#include "paddle/phi/backends/dynload/cupti.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/os_info.h"
namespace phi {
///////////////////////
// WARN: Under Development. Don't depend on it yet.
//////////////////////
class Event;
// DeviceTracer performs the following tasks:
// 1. Register cuda callbacks for various events: kernel, memcpy, etc.
// 2. Collect cuda statistics: start/end ts, memory, etc.
// 3. Generate a protobuf for further analysis.
class DeviceTracer {
public:
struct KernelRecord {
std::string name;
uint64_t start_ns;
uint64_t end_ns;
int64_t device_id;
int64_t stream_id;
uint32_t correlation_id;
};
struct CPURecord {
std::string name;
uint64_t start_ns;
uint64_t end_ns;
int64_t device_id;
uint64_t thread_id;
};
struct MemRecord {
std::string name;
uint64_t start_ns;
uint64_t end_ns;
int64_t device_id;
int64_t stream_id;
uint32_t correlation_id;
uint64_t bytes;
};
struct MemInfoRecord {
uint64_t start_ns;
uint64_t end_ns;
size_t bytes;
Place place;
uint64_t thread_id;
std::string alloc_in;
std::string free_in;
};
struct ActiveKindRecord {
std::string name;
uint64_t start_ns;
uint64_t end_ns;
int64_t device_id;
uint64_t thread_id;
uint32_t correlation_id;
};
virtual ~DeviceTracer() {}
// Needs to be called once before use.
virtual void Enable() = 0;
// Needs to be called once after use.
virtual void Disable() = 0;
// Needs to be called once before reuse.
virtual void Reset() = 0;
// Add a pair to correlate internal cuda id with high level
// annotation event(with string). So cuda statistics can be represented by
// human-readable annotations.
virtual void AddAnnotation(uint32_t id, Event* event) = 0;
virtual void AddAnnotations(
const std::map<uint64_t, ThreadEvents>& thr_events) = 0;
virtual void AddMemRecords(const std::string& name,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
int64_t stream_id,
uint32_t correlation_id,
uint64_t bytes) = 0;
virtual void AddCPURecords(const std::string& anno,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
uint64_t thread_id) = 0;
virtual void AddActiveKindRecords(const std::string& anno,
uint64_t start_ns,
uint64_t end_ns,
int64_t device_id,
uint64_t thread_id,
uint32_t correlation_id) = 0;
virtual void AddMemInfoRecord(uint64_t start_ns,
uint64_t end_ns,
size_t bytes,
const Place& place,
const std::string& alloc_in,
const std::string& free_in,
uint64_t thread_id) = 0;
// Add a cuda kernel stats. `correlation_id` will be mapped to annotation
// added before for human readability.
virtual void AddKernelRecords(std::string name,
uint64_t start,
uint64_t end,
int64_t device_id,
int64_t stream_id,
uint32_t correlation_id) = 0;
// Get a proto after done
virtual proto::Profile GetProfile() = 0;
// Generate a proto after done (Disabled).
virtual proto::Profile GenProfile(const std::string& profile_path) = 0;
// generate kernel elapsed time into Event
virtual void GenEventKernelCudaElapsedTime() = 0;
virtual bool IsEnabled() = 0;
};
// Get a DeviceTracer.
DeviceTracer* GetDeviceTracer();
// Set a name for the cuda kernel operation being launched by the thread.
void SetCurAnnotation(Event* event);
// Clear the name after the operation is done.
void ClearCurAnnotation();
// Current name of the operation being run in the thread.
std::string CurAnnotationName();
Event* CurAnnotation();
void SetCurBlock(int block_id);
void ClearCurBlock();
int BlockDepth();
// Set current thread id, so we can map the system thread id to thread id.
void RecordCurThreadId(uint64_t id);
} // namespace phi
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed 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.
#include "paddle/phi/api/profiler/event.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include "glog/logging.h"
#endif
namespace phi {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
CudaEvent::CudaEvent() {
#ifdef PADDLE_WITH_HIP
hipEventCreateWithFlags(&event_, flags_);
#else
cudaEventCreateWithFlags(&event_, flags_);
#endif
VLOG(4) << "CudaEvent " << event_;
}
CudaEvent::CudaEvent(unsigned int flags) : flags_(flags) {
#ifdef PADDLE_WITH_HIP
hipEventCreateWithFlags(&event_, flags_);
#else
cudaEventCreateWithFlags(&event_, flags_);
#endif
VLOG(4) << "CudaEvent " << event_;
}
bool CudaEvent::Query() {
#ifdef PADDLE_WITH_HIP
gpuError_t err = hipEventQuery(event_);
if (err == hipSuccess) {
return true;
}
if (err == hipErrorNotReady) {
return false;
}
#else
gpuError_t err = cudaEventQuery(event_);
if (err == cudaSuccess) {
return true;
}
if (err == cudaErrorNotReady) {
return false;
}
#endif
PADDLE_ENFORCE_GPU_SUCCESS(err);
return false;
}
float CudaEvent::ElapsedTime(CudaEvent *end_event) {
float milliseconds = 0;
#ifdef PADDLE_WITH_HIP
hipEventSynchronize(end_event->GetRawCudaEvent());
PADDLE_ENFORCE_GPU_SUCCESS(
hipEventElapsedTime(&milliseconds, event_, end_event->GetRawCudaEvent()));
#else
cudaEventSynchronize(end_event->GetRawCudaEvent());
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventElapsedTime(
&milliseconds, event_, end_event->GetRawCudaEvent()));
#endif
return milliseconds;
}
#endif
} // namespace phi
+190
View File
@@ -0,0 +1,190 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <functional>
#include <map>
#include <string>
#include <utility>
#include "paddle/phi/common/place.h"
#ifdef PADDLE_WITH_CUDA
#include <cuda_runtime.h>
#endif
#ifdef PADDLE_WITH_HIP
#include <hip/hip_runtime.h>
#endif
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include "paddle/phi/core/cuda_stream.h"
#elif defined(PADDLE_WITH_XPU)
#include "paddle/phi/core/xpu_cuda_stream.h"
#endif
namespace phi {
enum class EventType { kMark, kPushRange, kPopRange };
enum class EventRole {
kOrdinary, // only record op time with op type key
kInnerOp, // record op detail time with op type key
kUniqueOp, // record op detail time with op unique name key
kSpecial, // record event such as PE which is outer of thread local
};
class PADDLE_API Event {
public:
// The DeviceContext is used to get the cuda stream.
// If CPU profiling mode, can pass nullptr.
Event(EventType type,
std::string name,
uint32_t thread_id,
EventRole role = EventRole::kOrdinary,
std::string attr = "none");
const EventType &type() const;
Event *parent() const { return parent_; }
void set_parent(Event *parent) { parent_ = parent; }
std::string name() const { return name_; }
EventRole role() const { return role_; }
uint64_t thread_id() const { return thread_id_; }
void set_name(std::string name) { name_ = name; }
void set_role(EventRole role) { role_ = role; }
std::string attr() const { return attr_; }
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_XPU)
#ifndef PADDLE_WITH_CUPTI
gpuEvent_t event() const { return event_; }
int device() const { return device_; }
#endif
#endif
double CpuElapsedMs(const Event &e) const;
double CudaElapsedMs(const Event &e) const;
private:
EventType type_;
std::string name_{};
Event *parent_{nullptr};
uint64_t thread_id_;
EventRole role_{};
uint64_t cpu_ns_;
bool visited_status_{false};
std::string attr_;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_XPU)
#ifdef PADDLE_WITH_CUPTI
int64_t gpu_ns_ = 0;
public:
void AddCudaElapsedTime(int64_t start_ns, int64_t end_ns) {
gpu_ns_ += end_ns - start_ns;
}
private:
#else
gpuEvent_t event_ = nullptr;
int device_ = -1;
#endif
#endif
};
using EventWithStartNs = std::pair<Event *, uint64_t>;
using ThreadEvents = std::map<uint64_t, EventWithStartNs>;
class MemEvent {
public:
MemEvent(EventType type,
uint64_t start_ns,
uint64_t end_ns,
size_t bytes,
Place place,
int64_t thread_id,
const std::string &annotation)
: type_(type),
start_ns_(start_ns),
end_ns_(end_ns),
bytes_(bytes),
place_(place),
thread_id_(thread_id),
annotation_(annotation) {}
const EventType &type() const { return type_; }
uint64_t start_ns() const { return start_ns_; }
uint64_t end_ns() const { return end_ns_; }
size_t bytes() const { return bytes_; }
Place place() const { return place_; }
uint64_t thread_id() const { return thread_id_; }
const std::string &annotation() const { return annotation_; }
private:
EventType type_;
uint64_t start_ns_ = 0;
uint64_t end_ns_ = 0;
size_t bytes_;
Place place_;
uint64_t thread_id_;
std::string annotation_;
};
class CudaEvent {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
public:
PADDLE_API CudaEvent();
PADDLE_API explicit CudaEvent(unsigned int flags);
~CudaEvent() {
#ifdef PADDLE_WITH_HIP
hipEventDestroy(event_);
#else
cudaEventDestroy(event_);
#endif
}
void Record(gpuStream_t stream) {
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventRecord(event_, stream));
#else
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventRecord(event_, stream));
#endif
}
PADDLE_API bool Query();
PADDLE_API float ElapsedTime(CudaEvent *end_event);
void Synchronize() {
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventSynchronize(event_));
#else
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventSynchronize(event_));
#endif
}
gpuEvent_t GetRawCudaEvent() { return event_; }
private:
#ifdef PADDLE_WITH_HIP
unsigned int flags_ = hipEventDefault;
#else
unsigned int flags_ = cudaEventDefault;
#endif
gpuEvent_t event_;
#endif
};
} // namespace phi
+97
View File
@@ -0,0 +1,97 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <string>
#include "paddle/phi/api/profiler/event.h"
#include "paddle/phi/api/profiler/trace_event.h"
#include "paddle/utils/test_macros.h"
namespace phi {
// Default tracing level.
// It is Recommended to set the level explicitly.
static constexpr uint32_t kDefaultTraceLevel = 4;
// Host event tracing. A trace starts when an object of this class is created
// and stops when the object is destroyed.
// Chrome Trace Viewer Format: Duration Event/Complete Event
class PADDLE_API RecordEvent {
public:
static bool IsEnabled();
/**
* @param name If your string argument has a longer lifetime (e.g.: string
* literal, static variables, etc) than the event, use 'const char* name'.
* Do your best to avoid using 'std::string' as the argument type. It will
* cause deep-copy to harm performance.
* @param type Classification which is used to instruct the profiling
* data statistics.
* @param level Used to filter events, works like glog VLOG(level).
* RecordEvent will works if HostTraceLevel >= level.
*/
explicit RecordEvent(
const std::string& name,
const TracerEventType type = TracerEventType::UserDefined,
uint32_t level = kDefaultTraceLevel,
const EventRole role = EventRole::kOrdinary);
/**
* @param name It is the caller's responsibility to manage the underlying
* storage. RecordEvent stores the pointer.
* @param type Classification which is used to instruct the profiling
* data statistics.
* @param level Used to filter events, works like glog VLOG(level).
* RecordEvent will works if HostTraceLevel >= level.
*/
explicit RecordEvent(
const char* name,
const TracerEventType type = TracerEventType::UserDefined,
uint32_t level = kDefaultTraceLevel,
const EventRole role = EventRole::kOrdinary);
RecordEvent(const std::string& name,
const std::string& attr,
const TracerEventType type = TracerEventType::UserDefined,
uint32_t level = kDefaultTraceLevel,
const EventRole role = EventRole::kOrdinary);
// Stop event tracing explicitly before the object goes out of scope.
// Sometimes it's inconvenient to use RAII
void End();
~RecordEvent() { End(); }
private:
void OriginalConstruct(const std::string& name,
const EventRole role,
const std::string& attr);
bool is_enabled_{false};
bool is_pushed_{false};
// Event name
std::string* name_{nullptr};
const char* shallow_copy_name_{nullptr};
uint64_t start_ns_;
// Need to distinguish name by op type, block_id, program_id and perhaps
// different kernel invocations within an op.
// std::string full_name_;
EventRole role_{EventRole::kOrdinary};
TracerEventType type_{TracerEventType::UserDefined};
std::string* attr_{nullptr};
bool finished_{false};
};
} // namespace phi
@@ -0,0 +1,292 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed 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.
#pragma once
#include <string>
#include <type_traits>
#include <vector>
#include "paddle/common/macros.h"
#include "paddle/phi/common/thread_data_registry.h"
#include "paddle/phi/core/os_info.h"
namespace phi {
template <typename HeadType, typename... RestTypes>
struct ContainsStdString
: std::conditional_t<
std::is_same<
std::string,
std::remove_cv_t<std::remove_reference_t<HeadType>>>::value,
std::true_type,
ContainsStdString<RestTypes...>> {};
template <typename TailType>
struct ContainsStdString<TailType>
: std::is_same<std::string,
std::remove_cv_t<std::remove_reference_t<TailType>>> {};
template <typename EventType>
class EventContainer {
public:
EventContainer() {
event_blocks_ = cur_event_block_ = new EventBlock;
str_blocks_ = cur_str_block_ = new StringBlock;
}
~EventContainer() {
Reduce();
delete event_blocks_;
for (auto cur = str_blocks_; cur != nullptr;) {
auto next = cur->next;
delete cur;
cur = next;
}
}
DISABLE_COPY_AND_ASSIGN(EventContainer);
public:
// Record an event
template <typename... Args>
void Record(Args &&...args) {
DoRecord(ContainsStdString<Args...>(), std::forward<Args>(args)...);
}
// Get all events and clear the container
std::vector<EventType> Reduce();
// Return a buffer to store the string attribute of Event.
// HostEventRecorder locates in the static data section.
// So it's safe to use arena to avoid fragmented allocations.
char *GetStrBufFromArena(size_t size) { return GetStringStorage(size); }
private:
struct EventBlock {
union InitDeferredEvent {
InitDeferredEvent() {}
~InitDeferredEvent() {}
EventType event;
};
static constexpr size_t kBlockSize = 1 << 24; // 16 MB
static constexpr size_t kAvailSize =
kBlockSize - sizeof(size_t) - sizeof(nullptr);
static constexpr size_t kNumEvents = kAvailSize / sizeof(InitDeferredEvent);
static constexpr size_t kPadSize =
kAvailSize - kNumEvents * sizeof(InitDeferredEvent);
static constexpr size_t kMinimumEventsPerBlock = 1024;
static_assert(
kNumEvents >= kMinimumEventsPerBlock,
"EventType is too large for kBlockSize, make kBlockSize larger");
size_t offset = 0;
EventBlock *next = nullptr;
InitDeferredEvent events[kNumEvents];
char padding[kPadSize];
};
static_assert(sizeof(EventBlock) == EventBlock::kBlockSize,
"sizeof EventBlock must equal to kBlockSize");
struct StringBlock {
static constexpr size_t kBlockSize = 1 << 22; // 4 MB
static constexpr size_t kAvailSize =
kBlockSize - sizeof(size_t) - sizeof(nullptr);
size_t offset = 0;
StringBlock *next = nullptr;
char storage[kAvailSize];
};
static_assert(sizeof(StringBlock) == StringBlock::kBlockSize,
"sizeof StringBlock must equal to kBlockSize");
// Record an event with string arguments
template <typename... Args>
void DoRecord(std::true_type, Args &&...args) {
auto *storage = GetEventStorage();
std::function<void *(size_t)> allocator = [this](size_t size) {
return GetStrBufFromArena(size);
};
new (storage) EventType(allocator, std::forward<Args>(args)...);
}
// Record an event without any string argument
template <typename... Args>
void DoRecord(std::false_type, Args &&...args) {
auto *storage = GetEventStorage();
new (storage) EventType(std::forward<Args>(args)...);
}
EventType *GetEventStorage();
char *GetStringStorage(size_t sz);
EventBlock *event_blocks_ = nullptr;
EventBlock *cur_event_block_ = nullptr;
StringBlock *str_blocks_ = nullptr;
StringBlock *cur_str_block_ = nullptr;
};
template <typename EventType>
std::vector<EventType> EventContainer<EventType>::Reduce() {
std::vector<EventType> all_events;
size_t event_cnt = 0;
for (auto cur = event_blocks_; cur != nullptr; cur = cur->next) {
event_cnt += cur->offset;
}
all_events.reserve(event_cnt);
for (auto cur = event_blocks_; cur != nullptr;) {
for (size_t i = 0; i < cur->offset; ++i) {
all_events.emplace_back(cur->events[i].event);
}
auto next = cur->next;
delete cur;
cur = next;
}
event_blocks_ = cur_event_block_ = new EventBlock;
return all_events;
}
template <typename EventType>
EventType *EventContainer<EventType>::GetEventStorage() {
if (UNLIKELY(cur_event_block_->offset >=
EventBlock::kNumEvents)) { // another block
cur_event_block_->next = new EventBlock;
cur_event_block_ = cur_event_block_->next;
}
auto &obj = cur_event_block_->events[cur_event_block_->offset].event;
++cur_event_block_->offset;
return &obj;
}
template <typename EventType>
char *EventContainer<EventType>::GetStringStorage(size_t sz) {
if (UNLIKELY(cur_str_block_->offset + sz >
StringBlock::kAvailSize)) { // another block
cur_str_block_->next = new StringBlock;
cur_str_block_ = cur_str_block_->next;
}
char *storage = cur_str_block_->storage + cur_str_block_->offset;
cur_str_block_->offset += sz;
return storage;
}
template <typename EventType>
struct ThreadEventSection {
std::string thread_name;
uint64_t thread_id;
std::vector<EventType> events;
};
template <typename EventType>
class ThreadEventRecorder {
public:
ThreadEventRecorder() {
thread_id_ = GetCurrentThreadSysId();
thread_name_ = GetCurrentThreadName();
}
DISABLE_COPY_AND_ASSIGN(ThreadEventRecorder);
public:
// Forward call to EventContainer::Record
template <typename... Args>
void RecordEvent(Args &&...args) {
base_evt_cntr_.Record(std::forward<Args>(args)...);
}
ThreadEventSection<EventType> GatherEvents() {
ThreadEventSection<EventType> thr_sec;
thr_sec.thread_name = thread_name_;
thr_sec.thread_id = thread_id_;
thr_sec.events = std::move(base_evt_cntr_.Reduce());
return thr_sec;
}
private:
uint64_t thread_id_;
std::string thread_name_;
EventContainer<EventType> base_evt_cntr_;
};
template <typename EventType>
struct HostEventSection {
std::string process_name;
uint64_t process_id;
std::vector<ThreadEventSection<EventType>> thr_sections;
};
template <typename EventType>
class HostEventRecorder {
public:
// singleton
static HostEventRecorder &GetInstance() {
static HostEventRecorder instance;
return instance;
}
// thread-safe
// If your string argument has a longer lifetime than the Event,
// use 'const char*'. e.g.: string literal, op name, etc.
// Do your best to avoid using 'std::string' as the argument type.
// It will cause deep-copy to harm performance.
template <typename... Args>
void RecordEvent(Args &&...args) {
// Get thread local ThreadEventRecorder
// If not exists, we create a new one.
// Both HostEventRecorder and thread-local variable in
// ThreadEventRecorderRegistry keep the shared pointer. We add this to
// prevent ThreadEventRecorder being destroyed by thread-local variable in
// ThreadEventRecorderRegistry and lose data.
if (GetThreadLocalRecorder()->get() == nullptr) {
std::shared_ptr<ThreadEventRecorder<EventType>>
thread_event_recorder_ptr =
std::make_shared<ThreadEventRecorder<EventType>>();
*(GetThreadLocalRecorder()) = thread_event_recorder_ptr;
thr_recorders_.push_back(thread_event_recorder_ptr);
}
(*GetThreadLocalRecorder())->RecordEvent(std::forward<Args>(args)...);
}
// thread-unsafe, make sure make sure there is no running tracing.
// Poor performance, call it at the ending
HostEventSection<EventType> GatherEvents() {
HostEventSection<EventType> host_sec;
host_sec.process_id = GetProcessId();
host_sec.thr_sections.reserve(thr_recorders_.size());
for (auto &v : thr_recorders_) {
host_sec.thr_sections.emplace_back(std::move(v->GatherEvents()));
}
return host_sec;
}
private:
using ThreadEventRecorderRegistry =
phi::ThreadDataRegistry<std::shared_ptr<ThreadEventRecorder<EventType>>>;
HostEventRecorder() = default;
DISABLE_COPY_AND_ASSIGN(HostEventRecorder);
std::shared_ptr<ThreadEventRecorder<EventType>> *GetThreadLocalRecorder() {
return ThreadEventRecorderRegistry::GetInstance()
.GetMutableCurrentThreadData();
}
// Hold all thread-local ThreadEventRecorders
// ThreadEventRecorderRegistry and HostEventRecorder both take care of this
// shared pointer. We add this to prevent ThreadEventRecorder being destroyed
// by thread-local variable in ThreadEventRecorderRegistry and lose data.
std::vector<std::shared_ptr<ThreadEventRecorder<EventType>>> thr_recorders_;
};
} // namespace phi
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed 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.
#pragma once
namespace phi {
class HostTraceLevel {
public:
static constexpr int64_t kDisabled = -1;
static HostTraceLevel& GetInstance() {
static HostTraceLevel instance;
return instance;
}
bool NeedTrace(uint32_t level) {
return trace_level_ >= static_cast<int64_t>(level);
}
void SetLevel(int64_t trace_level) { trace_level_ = trace_level; }
private:
// Verbose trace level, works like VLOG(level)
int trace_level_ = kDisabled;
};
struct HostTracerOptions {
uint32_t trace_level = 0;
};
} // namespace phi
+295
View File
@@ -0,0 +1,295 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#include "paddle/phi/api/profiler/profiler.h"
#include <mutex> // NOLINT
#include <random>
#include <sstream>
#include <string>
#include <type_traits>
#include "glog/logging.h"
#include "paddle/phi/api/profiler/common_event.h"
#include "paddle/phi/api/profiler/device_tracer.h"
#include "paddle/phi/api/profiler/host_event_recorder.h"
#include "paddle/phi/api/profiler/host_tracer.h"
#include "paddle/phi/api/profiler/profiler_helper.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/core/os_info.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_XPU)
#include "paddle/phi/backends/dynload/nvtx.h"
#endif
PHI_DEFINE_bool(enable_host_event_recorder_hook,
false,
"enable HostEventRecorder, hook Profiler");
PHI_DEFINE_bool(enable_record_op_info,
false,
"enable operator supplement info recorder");
namespace phi {
ProfilerState ProfilerHelper::g_state = ProfilerState::kDisabled;
bool ProfilerHelper::g_enable_nvprof_hook = false;
thread_local uint64_t ProfilerHelper::g_thread_id;
uint32_t ProfilerHelper::g_next_thread_id = 0;
std::mutex ProfilerHelper::g_all_event_lists_mutex;
std::list<std::shared_ptr<EventList<Event>>> ProfilerHelper::g_all_event_lists;
thread_local std::shared_ptr<EventList<Event>> ProfilerHelper::g_event_list;
std::list<std::shared_ptr<EventList<MemEvent>>>
ProfilerHelper::g_all_mem_event_lists;
thread_local std::shared_ptr<EventList<MemEvent>>
ProfilerHelper::g_mem_event_list;
std::mutex ProfilerHelper::g_all_mem_event_lists_mutex;
Event::Event(EventType type,
std::string name,
uint32_t thread_id,
EventRole role,
std::string attr)
: type_(type),
name_(name),
thread_id_(thread_id),
role_(role),
attr_(attr) {
cpu_ns_ = GetTimeInNsec();
}
const EventType &Event::type() const { return type_; }
double Event::CpuElapsedMs(const Event &e) const {
return (static_cast<double>(e.cpu_ns_ - cpu_ns_)) / (1000000.0);
}
double Event::CudaElapsedMs(const Event &e) const {
#ifdef PADDLE_WITH_CUPTI
return static_cast<double>(gpu_ns_) / 1000000.0;
#else
LOG_FIRST_N(WARNING, 1) << "CUDA CUPTI is not enabled";
return 0;
#endif
}
Event *PushEvent(const std::string &name,
const EventRole role,
std::string attr) {
return GetEventList().Record(
EventType::kPushRange, name, ProfilerHelper::g_thread_id, role, attr);
}
void PopEvent(const std::string &name, const EventRole role, std::string attr) {
GetEventList().Record(
EventType::kPopRange, name, ProfilerHelper::g_thread_id, role, attr);
}
RecordEvent::RecordEvent(const char *name,
const TracerEventType type,
uint32_t level,
const EventRole role) {
#ifndef _WIN32
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_XPU)
if (ProfilerHelper::g_enable_nvprof_hook) {
dynload::nvtxRangePushA(name);
is_pushed_ = true;
}
#endif
#endif
if (UNLIKELY(HostTraceLevel::GetInstance().NeedTrace(level) == false)) {
return;
}
if (FLAGS_enable_host_event_recorder_hook == false) {
if (ProfilerHelper::g_state !=
ProfilerState::kDisabled) { // avoid temp string
if (type == TracerEventType::Operator ||
type == TracerEventType::OperatorInner ||
type == TracerEventType::UserDefined) {
OriginalConstruct(name, role, "none");
}
}
return;
}
is_enabled_ = true;
shallow_copy_name_ = name;
role_ = role;
type_ = type;
start_ns_ = PosixInNsec();
}
RecordEvent::RecordEvent(const std::string &name,
const TracerEventType type,
uint32_t level,
const EventRole role) {
#ifndef _WIN32
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_XPU)
if (ProfilerHelper::g_enable_nvprof_hook) {
dynload::nvtxRangePushA(name.c_str());
is_pushed_ = true;
}
#endif
#endif
if (UNLIKELY(HostTraceLevel::GetInstance().NeedTrace(level) == false)) {
return;
}
if (FLAGS_enable_host_event_recorder_hook == false) {
if (type == TracerEventType::Operator ||
type == TracerEventType::OperatorInner ||
type == TracerEventType::UserDefined) {
OriginalConstruct(name, role, "none");
}
return;
}
is_enabled_ = true;
name_ = new std::string(name);
role_ = role;
type_ = type;
start_ns_ = PosixInNsec();
}
RecordEvent::RecordEvent(const std::string &name,
const std::string &attr,
const TracerEventType type,
uint32_t level,
const EventRole role) {
#ifndef _WIN32
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_XPU)
if (ProfilerHelper::g_enable_nvprof_hook) {
dynload::nvtxRangePushA(name.c_str());
is_pushed_ = true;
}
#endif
#endif
if (UNLIKELY(HostTraceLevel::GetInstance().NeedTrace(level) == false)) {
return;
}
if (FLAGS_enable_host_event_recorder_hook == false) {
if (type == TracerEventType::Operator ||
type == TracerEventType::OperatorInner ||
type == TracerEventType::UserDefined) {
OriginalConstruct(name, role, attr);
}
return;
}
is_enabled_ = true;
type_ = type;
name_ = new std::string(name);
start_ns_ = PosixInNsec();
attr_ = new std::string(attr);
}
void RecordEvent::OriginalConstruct(const std::string &name,
const EventRole role,
const std::string &attr) {
if (ProfilerHelper::g_state == ProfilerState::kDisabled || name.empty())
return;
// do some initialization
name_ = new std::string(name);
start_ns_ = PosixInNsec();
role_ = role;
attr_ = new std::string(attr);
is_enabled_ = true;
// lock is not needed, the code below is thread-safe
// Maybe need the same push/pop behavior.
Event *e = PushEvent(name, role, attr);
SetCurAnnotation(e);
*name_ = e->name();
}
void RecordEvent::End() {
#ifndef _WIN32
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_XPU)
if (ProfilerHelper::g_enable_nvprof_hook && is_pushed_) {
dynload::nvtxRangePop();
is_pushed_ = false;
}
#endif
#endif
if (LIKELY(FLAGS_enable_host_event_recorder_hook && is_enabled_)) {
uint64_t end_ns = PosixInNsec();
if (LIKELY(shallow_copy_name_ != nullptr)) {
HostEventRecorder<CommonEvent>::GetInstance().RecordEvent(
shallow_copy_name_, start_ns_, end_ns, role_, type_);
} else if (name_ != nullptr) {
if (attr_ == nullptr) {
HostEventRecorder<CommonEvent>::GetInstance().RecordEvent(
*name_, start_ns_, end_ns, role_, type_);
} else {
HostEventRecorder<CommonEvent>::GetInstance().RecordEvent(
*name_, start_ns_, end_ns, role_, type_, *attr_);
delete attr_;
}
delete name_;
}
// use this flag to avoid double End();
is_enabled_ = false;
return;
}
if (ProfilerHelper::g_state == ProfilerState::kDisabled || !is_enabled_)
return;
// lock is not needed, the code below is thread-safe
DeviceTracer *tracer = GetDeviceTracer();
if (tracer) {
uint64_t end_ns = PosixInNsec();
tracer->AddCPURecords(CurAnnotationName(),
start_ns_,
end_ns,
BlockDepth(),
ProfilerHelper::g_thread_id);
}
ClearCurAnnotation();
PopEvent(*name_, role_);
delete name_;
delete attr_;
// use this flag to avoid double End();
is_enabled_ = false;
}
bool RecordEvent::IsEnabled() {
return FLAGS_enable_host_event_recorder_hook ||
ProfilerHelper::g_enable_nvprof_hook ||
ProfilerHelper::g_state != ProfilerState::kDisabled;
}
RecordOpInfoSupplement::RecordOpInfoSupplement(
const std::string &type,
const std::vector<std::pair<const char *, std::vector<DDim>>> &input_shapes,
const AttributeMap &attrs) {
if (FLAGS_enable_host_event_recorder_hook == false) {
return;
}
if (IsEnabled() == false) {
return;
}
uint64_t op_id = 0;
HostEventRecorder<OperatorSupplementOriginEvent>::GetInstance().RecordEvent(
PosixInNsec(), type, input_shapes, attrs, op_id);
}
bool RecordOpInfoSupplement::IsEnabled() { return FLAGS_enable_record_op_info; }
void EnableOpInfoRecorder() { FLAGS_enable_record_op_info = true; }
void DisableOpInfoRecorder() { FLAGS_enable_record_op_info = false; }
} // namespace phi
+95
View File
@@ -0,0 +1,95 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <forward_list>
#include <list>
#include <map>
#include <memory>
#include <mutex> // NOLINT
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "paddle/common/flags.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
COMMON_DECLARE_bool(enable_host_event_recorder_hook);
namespace phi {
enum class ProfilerState {
kDisabled, // disabled state
kCPU, // CPU profiling state
kCUDA, // GPU profiling state
kAll, // Profile both CPU and GPU. (Currently experimental).
};
// it is the flag to control to print the profiling result
enum class TracerOption {
kDefault, // print the different op type profiling result
kOpDetail, // print the detail profiling result of different op type
kAllOpDetail, // print the detail profiling result of different op name
};
template <typename T>
struct EventList {
constexpr static size_t kMB = 1024 * 1024;
constexpr static size_t kEventBlockSize = 16 * kMB;
constexpr static size_t kEventSize = sizeof(T);
constexpr static size_t kEventAlign = alignof(T);
constexpr static size_t kNumBlock =
kEventBlockSize /
((kEventSize + kEventAlign - 1) / kEventAlign * kEventAlign);
template <typename... Args>
T* Record(Args&&... args) {
if (event_blocks.empty() || event_blocks.front().size() == kNumBlock) {
event_blocks.emplace_front();
event_blocks.front().reserve(kNumBlock);
}
event_blocks.front().emplace_back(std::forward<Args>(args)...);
return &event_blocks.front().back();
}
std::vector<T> Reduce() {
std::vector<T> result;
for (auto& block : event_blocks) {
result.insert(result.begin(),
std::make_move_iterator(block.begin()),
std::make_move_iterator(block.end()));
}
event_blocks.clear();
return result;
}
void Clear() { event_blocks.clear(); }
std::forward_list<std::vector<T>> event_blocks;
};
PADDLE_API Event* PushEvent(const std::string& name,
const EventRole role,
const std::string attr = "none");
PADDLE_API void PopEvent(const std::string& name,
const EventRole role,
const std::string attr = "none");
PADDLE_API void EnableOpInfoRecorder();
PADDLE_API void DisableOpInfoRecorder();
} // namespace phi
+61
View File
@@ -0,0 +1,61 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
Licensed 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. */
syntax = "proto2";
package phi.proto;
message MemCopy { optional uint64 bytes = 1; }
message Event {
enum EventType {
CPU = 0;
GPUKernel = 1;
NPUKernel = 2;
}
optional EventType type = 8;
optional string name = 1;
optional uint64 start_ns = 2;
optional uint64 end_ns = 3;
// When positive, it represents gpu id. When -1, it represents CPU.
optional int64 device_id = 5;
optional uint64 sub_device_id = 6;
optional MemCopy memcopy = 7;
optional string detail_info = 9;
}
message MemEvent {
enum Place {
CUDAPlace = 0;
CPUPlace = 1;
CUDAPinnedPlace = 2;
XPUPlace = 3;
NPUPlace = 4;
}
optional uint64 start_ns = 1;
optional uint64 end_ns = 2;
optional uint64 bytes = 3;
optional Place place = 4;
optional uint64 thread_id = 5;
optional uint32 device_id = 6;
optional string alloc_in = 7;
optional string free_in = 8;
}
message Profile {
repeated Event events = 1;
optional uint64 start_ns = 2;
optional uint64 end_ns = 3;
repeated MemEvent mem_events = 4;
}
+82
View File
@@ -0,0 +1,82 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <algorithm>
#include <iomanip>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <mutex> // NOLINT
#include <random>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "paddle/phi/api/profiler/device_tracer.h"
#include "paddle/phi/api/profiler/profiler.h"
namespace phi {
class ProfilerHelper {
public:
// The profiler state, the initial value is ProfilerState::kDisabled
static ProfilerState g_state;
// To hook RecordEvent's events, use it to nvtx timeline
static bool g_enable_nvprof_hook;
// The thread local event list only can be accessed by the specific thread
// The thread index of each thread
static thread_local uint64_t g_thread_id;
// The g_next_thread_id is a global counter for threads, by the g_thread_id
// and g_next_thread_id, we can know how many threads have created EventList.
static uint32_t g_next_thread_id;
// The global mutex
static std::mutex g_all_event_lists_mutex;
// The total event lists of all threads
static std::list<std::shared_ptr<EventList<Event>>> g_all_event_lists;
// The thread local event list only can be accessed by the specific thread
static thread_local std::shared_ptr<EventList<Event>> g_event_list;
static std::list<std::shared_ptr<EventList<MemEvent>>> g_all_mem_event_lists;
static thread_local std::shared_ptr<EventList<MemEvent>> g_mem_event_list;
static std::mutex g_all_mem_event_lists_mutex;
};
inline uint64_t GetTimeInNsec() {
using clock = std::conditional<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock>::type;
return std::chrono::duration_cast<std::chrono::nanoseconds>(
clock::now().time_since_epoch())
.count();
}
inline EventList<Event> &GetEventList() {
if (!ProfilerHelper::g_event_list) {
std::lock_guard<std::mutex> guard(ProfilerHelper::g_all_event_lists_mutex);
ProfilerHelper::g_event_list = std::make_shared<EventList<Event>>();
ProfilerHelper::g_thread_id = ProfilerHelper::g_next_thread_id++;
ProfilerHelper::g_all_event_lists.emplace_front(
ProfilerHelper::g_event_list);
RecordCurThreadId(ProfilerHelper::g_thread_id);
}
return *ProfilerHelper::g_event_list;
}
} // namespace phi
@@ -0,0 +1,39 @@
/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <map>
#include <string>
#include <utility>
#include "paddle/phi/core/attribute.h"
#include "paddle/phi/core/ddim.h"
namespace phi {
class RecordOpInfoSupplement {
public:
PADDLE_API static bool IsEnabled();
RecordOpInfoSupplement() = default;
explicit RecordOpInfoSupplement(
const std::string& type,
const std::vector<std::pair<const char*, std::vector<DDim>>>&
input_shapes,
const AttributeMap& attrs);
};
} // namespace phi
+347
View File
@@ -0,0 +1,347 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace phi {
#define FOR_EACH_TRACER_EVENT_TYPES(_) \
/* Used to mark operator record */ \
_(Operator) \
/* Used to mark dataloader record */ \
_(Dataloader) \
/* Used to mark profile step record */ \
_(ProfileStep) \
/* Used to mark cuda runtime record returned by cupti */ \
_(CudaRuntime) \
/* Used to mark kernel computation record returned by cupti */ \
_(Kernel) \
/* Used to mark memcpy record returned by cupti */ \
_(Memcpy) \
/* Used to mark memset record returned by cupti */ \
_(Memset) \
/* Used to mark record defined by user */ \
_(UserDefined) \
/* Used to mark operator detail, (such as infer shape, compute) */ \
_(OperatorInner) \
/* Used to mark model training or testing perspective, forward process */ \
_(Forward) \
/* Used to mark model training perspective, backward process */ \
_(Backward) \
/* Used to mark model training perspective, optimization process */ \
_(Optimization) \
/* Used to mark distributed training perspective */ \
_(Communication) \
/* Used to mark python api */ \
_(PythonOp) \
/* Used to mark python level user-defined */ \
_(PythonUserDefined) \
/* Used to mark kernel call in dynamic graph mode */ \
_(DygraphKernelLaunch) \
/* Used to mark kernel call in static graph mode */ \
_(StaticKernelLaunch)
enum class TracerEventType {
#define DEFINE_ENUM_ITEM(name) name,
FOR_EACH_TRACER_EVENT_TYPES(DEFINE_ENUM_ITEM)
#undef DEFINE_ENUM_ITEM
NumTypes
};
#define FOR_EACH_TRACER_MEM_EVENT_TYPES(_) \
/* Used to mark memory allocation which is managed by paddle */ \
_(Allocate) \
/* Used to mark memory free which is managed by paddle */ \
_(Free) \
/* Used to mark reserved memory allocation which is applied from device. */ \
_(ReservedAllocate) \
/* Used to mark reserved memory free which is released to device. */ \
_(ReservedFree)
enum class TracerMemEventType {
#define DEFINE_ENUM_ITEM(name) name,
FOR_EACH_TRACER_MEM_EVENT_TYPES(DEFINE_ENUM_ITEM)
#undef DEFINE_ENUM_ITEM
NumTypes
};
struct KernelEventInfo {
// The X-dimension block size for the kernel.
uint32_t block_x;
// The Y-dimension block size for the kernel.
uint32_t block_y;
// The Z-dimension grid size for the kernel.
uint32_t block_z;
// X-dimension of a grid.
uint32_t grid_x;
// Y-dimension of a grid.
uint32_t grid_y;
// Z-dimension of a grid.
uint32_t grid_z;
// The dynamic shared memory reserved for the kernel, in bytes.
uint32_t dynamic_shared_memory;
// The static shared memory allocated for the kernel, in bytes.
uint32_t static_shared_memory;
// The number of registers required for each thread executing the kernel.
uint32_t registers_per_thread;
// The amount of local memory reserved for each thread, in bytes.
uint32_t local_memory_per_thread;
// The total amount of local memory reserved for the kernel, in bytes.
uint32_t local_memory_total;
// The timestamp when the kernel is queued up in the command buffer, in ns.
// This timestamp is not collected by default. Use API
// cuptiActivityEnableLatencyTimestamps() to enable collection.
uint64_t queued;
// The timestamp when the command buffer containing the kernel launch is
// submitted to the GPU, in ns.
// This timestamp is not collected by default. Use API
// cuptiActivityEnableLatencyTimestamps() to enable collection.
uint64_t submitted;
// The completed timestamp for the kernel execution, in ns.
uint64_t completed;
float blocks_per_sm;
float warps_per_sm;
// theoretical achieved occupancy
float occupancy;
};
static constexpr size_t kMemKindMaxLen = 50;
struct MemcpyEventInfo {
// The number of bytes transferred by the memory copy.
uint64_t num_bytes;
// The kind of the memory copy.
// Each kind represents the source and destination targets of a memory copy.
// Targets are host, device, and array. Refer to CUpti_ActivityMemcpyKind
char copy_kind[kMemKindMaxLen];
// The source memory kind read by the memory copy.
// Each kind represents the type of the memory accessed by a memory
// operation/copy. Refer to CUpti_ActivityMemoryKind
char src_kind[kMemKindMaxLen];
// The destination memory kind read by the memory copy.
char dst_kind[kMemKindMaxLen];
};
struct MemsetEventInfo {
// The number of bytes being set by the memory set.
uint64_t num_bytes;
// The memory kind of the memory set. Refer to CUpti_ActivityMemoryKind
char memory_kind[kMemKindMaxLen];
// the value being assigned to memory by the memory set.
uint32_t value;
};
struct HostTraceEvent {
HostTraceEvent() = default;
HostTraceEvent(const std::string& name,
TracerEventType type,
uint64_t start_ns,
uint64_t end_ns,
uint64_t process_id,
uint64_t thread_id)
: name(name),
type(type),
start_ns(start_ns),
end_ns(end_ns),
process_id(process_id),
thread_id(thread_id) {}
// record name
std::string name;
// record type, one of TracerEventType
TracerEventType type;
// start timestamp of the record
uint64_t start_ns;
// end timestamp of the record
uint64_t end_ns;
// process id of the record
uint64_t process_id;
// thread id of the record
uint64_t thread_id;
};
struct RuntimeTraceEvent {
RuntimeTraceEvent() = default;
RuntimeTraceEvent(const std::string& name,
uint64_t start_ns,
uint64_t end_ns,
uint64_t process_id,
uint64_t thread_id,
uint32_t correlation_id,
uint32_t callback_id)
: name(name),
start_ns(start_ns),
end_ns(end_ns),
process_id(process_id),
thread_id(thread_id),
correlation_id(correlation_id),
callback_id(callback_id) {}
// record name
std::string name;
// record type, one of TracerEventType
TracerEventType type{TracerEventType::CudaRuntime};
// start timestamp of the record
uint64_t start_ns;
// end timestamp of the record
uint64_t end_ns;
// process id of the record
uint64_t process_id;
// thread id of the record
uint64_t thread_id;
// correlation id, used for correlating async activities happened on device
uint32_t correlation_id;
// callback id, used to identify which cuda runtime api is called
uint32_t callback_id;
};
struct DeviceTraceEvent {
DeviceTraceEvent() = default;
DeviceTraceEvent(const std::string& name,
TracerEventType type,
uint64_t start_ns,
uint64_t end_ns,
uint64_t device_id,
uint64_t context_id,
uint64_t stream_id,
uint32_t correlation_id,
const KernelEventInfo& kernel_info)
: name(name),
type(type),
start_ns(start_ns),
end_ns(end_ns),
device_id(device_id),
context_id(context_id),
stream_id(stream_id),
correlation_id(correlation_id),
kernel_info(kernel_info) {}
DeviceTraceEvent(const std::string& name,
TracerEventType type,
uint64_t start_ns,
uint64_t end_ns,
uint64_t device_id,
uint64_t context_id,
uint64_t stream_id,
uint32_t correlation_id,
const MemcpyEventInfo& memcpy_info)
: name(name),
type(type),
start_ns(start_ns),
end_ns(end_ns),
device_id(device_id),
context_id(context_id),
stream_id(stream_id),
correlation_id(correlation_id),
memcpy_info(memcpy_info) {}
DeviceTraceEvent(const std::string& name,
TracerEventType type,
uint64_t start_ns,
uint64_t end_ns,
uint64_t device_id,
uint64_t context_id,
uint64_t stream_id,
uint32_t correlation_id,
const MemsetEventInfo& memset_info)
: name(name),
type(type),
start_ns(start_ns),
end_ns(end_ns),
device_id(device_id),
context_id(context_id),
stream_id(stream_id),
correlation_id(correlation_id),
memset_info(memset_info) {}
// record name
std::string name;
// record type, one of TracerEventType
TracerEventType type;
// start timestamp of the record
uint64_t start_ns;
// end timestamp of the record
uint64_t end_ns;
// device id
uint64_t device_id;
// context id
uint64_t context_id;
// stream id
uint64_t stream_id;
// correlation id, used for correlating async activities happened on device
uint32_t correlation_id;
// union, specific device record type has different detail information
union {
// used for TracerEventType::Kernel
KernelEventInfo kernel_info;
// used for TracerEventType::Memcpy
MemcpyEventInfo memcpy_info;
// used for TracerEventType::Memset
MemsetEventInfo memset_info;
};
};
struct MemTraceEvent {
MemTraceEvent() = default;
MemTraceEvent(uint64_t timestamp_ns,
uint64_t addr,
TracerMemEventType type,
uint64_t process_id,
uint64_t thread_id,
int64_t increase_bytes,
const std::string& place,
uint64_t current_allocated,
uint64_t current_reserved,
uint64_t peak_allocated,
uint64_t peak_reserved)
: timestamp_ns(timestamp_ns),
addr(addr),
type(type),
process_id(process_id),
thread_id(thread_id),
increase_bytes(increase_bytes),
place(place),
current_allocated(current_allocated),
current_reserved(current_reserved),
peak_allocated(peak_allocated),
peak_reserved(peak_reserved) {}
// timestamp of the record
uint64_t timestamp_ns;
// memory addr of allocation or free
uint64_t addr;
// memory manipulation type
TracerMemEventType type;
// process id of the record
uint64_t process_id;
// thread id of the record
uint64_t thread_id;
// increase bytes after this manipulation, allocation for sign +, free for
// sign -
int64_t increase_bytes;
// place
std::string place;
// current total allocated memory
uint64_t current_allocated;
// current total reserved memory
uint64_t current_reserved;
// current peak allocated memory
uint64_t peak_allocated;
// current peak reserved memory
uint64_t peak_reserved;
};
} // namespace phi
@@ -0,0 +1,75 @@
/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
licensed 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. */
#pragma once
#include <list>
#include <string>
#include <unordered_map>
#include "paddle/phi/api/profiler/trace_event.h"
namespace phi {
class TraceEventCollector {
public:
void AddHostEvent(HostTraceEvent&& event) { host_events_.push_back(event); }
void AddRuntimeEvent(RuntimeTraceEvent&& event) {
runtime_events_.push_back(event);
}
void AddDeviceEvent(DeviceTraceEvent&& event) {
device_events_.push_back(event);
}
void AddThreadName(uint64_t tid, const std::string& name) {
thread_names_[tid] = name;
}
void AddMemEvent(MemTraceEvent&& event) { mem_events_.push_back(event); }
const std::list<HostTraceEvent>& HostEvents() const { return host_events_; }
const std::list<RuntimeTraceEvent>& RuntimeEvents() const {
return runtime_events_;
}
const std::list<DeviceTraceEvent>& DeviceEvents() const {
return device_events_;
}
const std::list<MemTraceEvent>& MemEvents() const { return mem_events_; }
const std::unordered_map<uint64_t, std::string>& ThreadNames() const {
return thread_names_;
}
void ClearAll() {
thread_names_.clear();
host_events_.clear();
runtime_events_.clear();
device_events_.clear();
mem_events_.clear();
}
protected:
std::unordered_map<uint64_t, std::string> thread_names_;
std::list<HostTraceEvent> host_events_;
std::list<RuntimeTraceEvent> runtime_events_;
std::list<DeviceTraceEvent> device_events_;
std::list<MemTraceEvent> mem_events_;
};
} // namespace phi
+40
View File
@@ -0,0 +1,40 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed 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.
#pragma once
#include "paddle/phi/api/profiler/trace_event_collector.h"
namespace phi {
class TracerBase {
public:
// The state machine for a Tracer.
enum class TracerState { UNINITED, READY, STARTED, STOPPED };
virtual void PrepareTracing() { state_ = TracerState::READY; }
virtual void StartTracing() = 0;
virtual void StopTracing() = 0;
virtual void CollectTraceData(TraceEventCollector* collector) = 0;
virtual ~TracerBase() {}
protected:
TracerState state_ = TracerState::UNINITED;
};
} // namespace phi