Skip to content

Commit cdb6b46

Browse files
authored
Sdk profiler (#1274)
* sdk-profiler * fix lint * support lift * sync net module when profile * use Scope* * update use task name * fix * use std::unique_ptr<Event> * remove mmdeploy::graph link for c and transform * fix * fix * fix
1 parent 938ef53 commit cdb6b46

File tree

15 files changed

+4124
-3
lines changed

15 files changed

+4124
-3
lines changed

csrc/mmdeploy/apis/c/mmdeploy/common.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "common_internal.h"
44
#include "executor_internal.h"
55
#include "mmdeploy/core/mat.h"
6+
#include "mmdeploy/core/profiler.h"
67

78
mmdeploy_value_t mmdeploy_value_copy(mmdeploy_value_t value) {
89
if (!value) {
@@ -72,6 +73,19 @@ int mmdeploy_device_create(const char* device_name, int device_id, mmdeploy_devi
7273

7374
void mmdeploy_device_destroy(mmdeploy_device_t device) { delete (Device*)device; }
7475

76+
int mmdeploy_profiler_create(const char* path, mmdeploy_profiler_t* profiler) {
77+
*profiler = (mmdeploy_profiler_t) new profiler::Profiler(path);
78+
return MMDEPLOY_SUCCESS;
79+
}
80+
81+
void mmdeploy_profiler_destroy(mmdeploy_profiler_t profiler) {
82+
if (profiler) {
83+
auto p = (profiler::Profiler*)profiler;
84+
p->Release();
85+
delete p;
86+
}
87+
}
88+
7589
int mmdeploy_context_add(mmdeploy_context_t context, mmdeploy_context_type_t type, const char* name,
7690
const void* object) {
7791
auto& ctx = *Cast(context);
@@ -88,6 +102,12 @@ int mmdeploy_context_add(mmdeploy_context_t context, mmdeploy_context_type_t typ
88102
case MMDEPLOY_TYPE_MODEL:
89103
ctx["model"][name] = *Cast((const mmdeploy_model_t)object);
90104
break;
105+
case MMDEPLOY_TYPE_PROFILER: {
106+
const auto& profiler = *(profiler::Profiler*)object;
107+
profiler::Scope* root(profiler.scope());
108+
ctx["scope"] = root;
109+
break;
110+
}
91111
default:
92112
return MMDEPLOY_E_NOT_SUPPORTED;
93113
}

csrc/mmdeploy/apis/c/mmdeploy/common.h

+18
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ typedef enum mmdeploy_status_t {
5656

5757
typedef struct mmdeploy_device* mmdeploy_device_t;
5858

59+
typedef struct mmdeploy_profiler* mmdeploy_profiler_t;
60+
5961
typedef struct mmdeploy_mat_t {
6062
uint8_t* data;
6163
int height;
@@ -88,6 +90,7 @@ typedef enum mmdeploy_context_type_t {
8890
MMDEPLOY_TYPE_MODEL = 2,
8991
MMDEPLOY_TYPE_SCHEDULER = 3,
9092
MMDEPLOY_TYPE_MAT = 4,
93+
MMDEPLOY_TYPE_PROFILER = 5,
9194
} mmdeploy_context_type_t;
9295

9396
#if __cplusplus
@@ -123,6 +126,21 @@ MMDEPLOY_API int mmdeploy_device_create(const char* device_name, int device_id,
123126
*/
124127
MMDEPLOY_API void mmdeploy_device_destroy(mmdeploy_device_t device);
125128

129+
/**
130+
* Create profiler
131+
* @param path path to save the profile data
132+
* @param profiler handle for profiler, should be added to context and deleted by
133+
* mmdeploy_profiler_destroy
134+
* @return status of create
135+
*/
136+
MMDEPLOY_API int mmdeploy_profiler_create(const char* path, mmdeploy_profiler_t* profiler);
137+
138+
/**
139+
* Destroy profiler handle
140+
* @param profiler handle for profiler, profile data will be written to disk after this call
141+
*/
142+
MMDEPLOY_API void mmdeploy_profiler_destroy(mmdeploy_profiler_t profiler);
143+
126144
/**
127145
* Create context
128146
* @param context

csrc/mmdeploy/apis/cxx/mmdeploy/common.hpp

+23
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ class Device {
7676
std::shared_ptr<mmdeploy_device> device_;
7777
};
7878

79+
class Profiler {
80+
public:
81+
explicit Profiler(std::string_view path) : path_(path) {
82+
mmdeploy_profiler_t profiler{};
83+
auto ec = mmdeploy_profiler_create(path_.c_str(), &profiler);
84+
if (ec != MMDEPLOY_SUCCESS) {
85+
throw_exception(static_cast<ErrorCode>(ec));
86+
}
87+
profiler_.reset(profiler, [](auto p) { mmdeploy_profiler_destroy(p); });
88+
};
89+
90+
operator mmdeploy_profiler_t() const noexcept { return profiler_.get(); }
91+
92+
private:
93+
std::string path_;
94+
std::shared_ptr<mmdeploy_profiler> profiler_;
95+
};
96+
7997
class Mat {
8098
public:
8199
Mat() : desc_{} {}
@@ -187,6 +205,10 @@ class Context {
187205
mmdeploy_context_add(*this, MMDEPLOY_TYPE_DEVICE, nullptr, device);
188206
}
189207

208+
void Add(const Profiler& profiler) {
209+
mmdeploy_context_add(*this, MMDEPLOY_TYPE_PROFILER, nullptr, profiler);
210+
}
211+
190212
operator mmdeploy_context_t() const noexcept { return context_.get(); }
191213

192214
private:
@@ -199,6 +221,7 @@ using cxx::Context;
199221
using cxx::Device;
200222
using cxx::Mat;
201223
using cxx::Model;
224+
using cxx::Profiler;
202225
using cxx::Rect;
203226
using cxx::Scheduler;
204227

csrc/mmdeploy/core/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ set(SRCS
4040
utils/device_utils.cpp
4141
utils/formatter.cpp
4242
utils/stacktrace.cpp
43+
profiler.cpp
4344
)
4445

4546
mmdeploy_add_library(${PROJECT_NAME} ${SRCS})
4647

47-
4848
target_include_directories(${PROJECT_NAME}
4949
PUBLIC
5050
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/csrc>
5151
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/third_party/outcome>
52+
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/third_party/concurrentqueue>
5253
# TODO: remove dependency of `json`
5354
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/third_party/json>
5455
)

csrc/mmdeploy/core/profiler.cpp

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) OpenMMLab. All rights reserved.
2+
3+
#include "mmdeploy/core/profiler.h"
4+
5+
#include <iomanip>
6+
7+
namespace mmdeploy {
8+
namespace profiler {
9+
10+
Event* Scope::Add(Event::Type type, Index index, TimePoint time_point) {
11+
return profiler_->AddEvent({this, type, index, time_point});
12+
}
13+
14+
Scope* Scope::CreateScope(std::string_view name) {
15+
std::string full_name = name_ + (name_.empty() ? "" : "/") + std::string(name);
16+
auto node = children_.emplace_back(profiler_->CreateScope(full_name));
17+
node->parent_ = this;
18+
return node;
19+
}
20+
21+
void Scope::Dump(Scope* scope, std::ofstream& ofs) {
22+
ofs << scope->name_ << "\n";
23+
for (const auto& child : scope->children_) {
24+
Dump(child, ofs);
25+
}
26+
}
27+
28+
ScopedCounter::ScopedCounter(Scope* scope) {
29+
if (scope) {
30+
start_ = scope->Add(Event::kStart, scope->next_.fetch_add(1, std::memory_order_relaxed),
31+
Clock::now());
32+
}
33+
}
34+
35+
ScopedCounter::~ScopedCounter() {
36+
if (start_) {
37+
start_->scope->Add(Event::kEnd, start_->index, Clock::now());
38+
}
39+
}
40+
41+
Profiler::Profiler(std::string_view path) : path_(path) { root_ = CreateScope("root"); }
42+
43+
Scope* Profiler::CreateScope(std::string_view name) {
44+
auto& node = nodes_.emplace_back();
45+
node.profiler_ = this;
46+
node.name_ = name;
47+
return &node;
48+
}
49+
50+
Event* Profiler::AddEvent(Event e) {
51+
auto uptr = std::make_unique<Event>(e);
52+
Event* pe = uptr.get();
53+
events_.enqueue(std::move(uptr));
54+
return pe;
55+
}
56+
57+
void Profiler::Release() {
58+
std::ofstream ofs(path_);
59+
root_->Dump(ofs);
60+
ofs << "----\n";
61+
62+
std::unique_ptr<Event> item;
63+
std::vector<std::unique_ptr<Event>> vec;
64+
while (events_.try_dequeue(item)) {
65+
vec.push_back(std::move(item));
66+
}
67+
68+
std::sort(vec.begin(), vec.end(),
69+
[](const std::unique_ptr<Event>& a, const std::unique_ptr<Event>& b) {
70+
return a->time_point < b->time_point;
71+
});
72+
73+
for (int i = 0; i < vec.size(); i++) {
74+
ofs << vec[i]->scope->name_ << " " << vec[i]->type << " " << vec[i]->index << " "
75+
<< std::chrono::duration_cast<std::chrono::microseconds>(vec[i]->time_point - TimePoint{})
76+
.count()
77+
<< "\n";
78+
}
79+
}
80+
81+
} // namespace profiler
82+
83+
} // namespace mmdeploy

csrc/mmdeploy/core/profiler.h

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) OpenMMLab. All rights reserved.
2+
3+
#ifndef MMDEPLOY_CSRC_MMDEPLOY_CORE_PROFILER_H_
4+
#define MMDEPLOY_CSRC_MMDEPLOY_CORE_PROFILER_H_
5+
6+
#include <atomic>
7+
#include <chrono>
8+
#include <deque>
9+
#include <fstream>
10+
#include <iostream>
11+
#include <string>
12+
#include <string_view>
13+
#include <vector>
14+
15+
#include "concurrentqueue.h"
16+
#include "mmdeploy/core/logger.h"
17+
#include "mmdeploy/core/macro.h"
18+
#include "mmdeploy/core/value.h"
19+
20+
namespace mmdeploy {
21+
namespace profiler {
22+
23+
struct Profiler;
24+
struct Scope;
25+
26+
using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
27+
std::chrono::high_resolution_clock, std::chrono::steady_clock>;
28+
using TimePoint = Clock::time_point;
29+
using Index = uint64_t;
30+
31+
struct Event {
32+
enum Type { kStart, kEnd };
33+
Scope* scope;
34+
Type type;
35+
Index index;
36+
TimePoint time_point;
37+
};
38+
39+
struct MMDEPLOY_API Scope {
40+
Scope() = default;
41+
Scope(const Scope&) = delete;
42+
Scope(Scope&&) noexcept = delete;
43+
Scope& operator=(const Scope&) = delete;
44+
Scope& operator=(Scope&&) noexcept = delete;
45+
46+
Event* Add(Event::Type type, Index index, TimePoint time_point);
47+
48+
Scope* CreateScope(std::string_view name);
49+
50+
void Dump(Scope* scope, std::ofstream& ofs);
51+
void Dump(std::ofstream& ofs) { Dump(this, ofs); }
52+
53+
Profiler* profiler_{};
54+
Scope* parent_{};
55+
std::vector<Scope*> children_;
56+
std::atomic<Index> next_{};
57+
std::string name_;
58+
};
59+
60+
struct MMDEPLOY_API ScopedCounter {
61+
explicit ScopedCounter(Scope* scope);
62+
~ScopedCounter();
63+
64+
Event* start_{};
65+
};
66+
67+
struct MMDEPLOY_API Profiler {
68+
explicit Profiler(std::string_view path);
69+
Scope* CreateScope(std::string_view name);
70+
Event* AddEvent(Event e);
71+
Scope* scope() const noexcept { return root_; }
72+
void Release();
73+
74+
std::string path_;
75+
std::deque<Scope> nodes_;
76+
moodycamel::ConcurrentQueue<std::unique_ptr<Event>> events_;
77+
Scope* root_{};
78+
};
79+
80+
} // namespace profiler
81+
82+
MMDEPLOY_REGISTER_TYPE_ID(profiler::Scope*, 10);
83+
84+
} // namespace mmdeploy
85+
86+
#endif // MMDEPLOY_CSRC_MMDEPLOY_GRAPH_PROFILER_H_

csrc/mmdeploy/graph/inference.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "mmdeploy/archive/json_archive.h"
66
#include "mmdeploy/core/graph.h"
77
#include "mmdeploy/core/model.h"
8+
#include "mmdeploy/core/profiler.h"
89
#include "mmdeploy/graph/common.h"
910

1011
namespace mmdeploy::graph {
@@ -33,6 +34,12 @@ Result<unique_ptr<Node>> InferenceBuilder::BuildImpl() {
3334
context["model"] = std::move(model);
3435

3536
auto pipeline_config = from_json<Value>(json);
37+
if (context.contains("scope")) {
38+
auto net_cfg = pipeline_config["pipeline"]["tasks"][1];
39+
std::string net_name = net_cfg["name"].get<std::string>();
40+
auto scope = context["scope"].get_ref<profiler::Scope*&>()->CreateScope(net_name);
41+
context["scope"] = scope;
42+
}
3643
pipeline_config["context"] = context;
3744

3845
MMDEPLOY_INFO("{}", pipeline_config);

csrc/mmdeploy/graph/task.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Sender<Value> Task::Process(Sender<Value> input) {
1212
assert(v.is_array());
1313
// handle empty input
1414
if (v.front().empty()) {
15+
profiler::ScopedCounter counter(scope_);
1516
return TransferJust(*sched_, Value(Value::Array(v.size(), Value::kArray)));
1617
}
1718
if (v.front().is_array() && !is_batched_) {
@@ -24,6 +25,7 @@ Sender<Value> Task::Process(Sender<Value> input) {
2425
return Value{std::move(input), std::move(output)};
2526
})
2627
| Bulk(batch_size, [&](size_t index, Value& in_out) {
28+
profiler::ScopedCounter counter(scope_);
2729
const auto& input = in_out[0];
2830
auto& output = in_out[1];
2931
output[index] = module_->Process(input[index]).value();
@@ -33,8 +35,10 @@ Sender<Value> Task::Process(Sender<Value> input) {
3335
});
3436
// clang-format on
3537
} else {
36-
return DynamicBatch(TransferJust(*sched_, std::move(v)), batch_context_,
37-
[&](const Value& u) { return module_->Process(u).value(); });
38+
return DynamicBatch(TransferJust(*sched_, std::move(v)), batch_context_, [&](const Value& u) {
39+
profiler::ScopedCounter counter(scope_);
40+
return module_->Process(u).value();
41+
});
3842
}
3943
});
4044
}
@@ -63,6 +67,17 @@ inline Result<unique_ptr<Module>> CreateModule(const Value& config) {
6367
Result<unique_ptr<Node>> TaskBuilder::BuildImpl() {
6468
try {
6569
auto task = std::make_unique<Task>();
70+
if (auto scope = Maybe{config_} / "context" / "scope" / identity<profiler::Scope*>{}) {
71+
auto module_name = config_.value<std::string>("module", "");
72+
auto name = config_.value<std::string>("name", "");
73+
string scope_name = (name != "") ? name : module_name;
74+
task->scope_ = (*scope)->CreateScope(scope_name);
75+
config_["context"]["scope"] = task->scope_;
76+
if (module_name == "Transform") {
77+
task->scope_ = nullptr;
78+
}
79+
}
80+
6681
OUTCOME_TRY(task->module_, CreateModule(config_));
6782

6883
if (auto name = Maybe{config_} / "scheduler" / identity<string>{}) {

csrc/mmdeploy/graph/task.h

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#define MMDEPLOY_CSRC_GRAPH_TASK_H_
55

66
#include "mmdeploy/core/graph.h"
7+
#include "mmdeploy/core/profiler.h"
78

89
namespace mmdeploy::graph {
910

@@ -19,6 +20,7 @@ class Task : public Node {
1920
bool is_batched_{false};
2021
bool is_thread_safe_{false};
2122
dynamic_batch_t::context_t batch_context_;
23+
profiler::Scope* scope_{nullptr};
2224
};
2325

2426
class TaskBuilder : public Builder {

0 commit comments

Comments
 (0)