memory_chunk.cpp 10.3 KB
Newer Older
M
Megvii Engine Team 已提交
1
#include <array>
2 3 4 5 6 7
#include <map>
#include <vector>

#include "megbrain/imperative/utils/to_string.h"
#include "megbrain/utils/debug.h"

8
#include "./events.h"
9 10 11 12 13 14 15 16
#include "./formats.h"
#include "./states.h"

namespace mgb::imperative::profiler {

class XMLWriter {
private:
    std::vector<std::vector<std::string>> elements;
M
Megvii Engine Team 已提交
17

18 19 20 21 22 23 24 25 26 27 28 29 30 31
public:
    struct ElementGuard {
        XMLWriter* writer;
        std::string name;
        std::vector<std::pair<std::string, std::string>> attrs;

        template <typename T>
        ElementGuard& attr(std::string key, T&& value) {
            attrs.push_back({key, mgb::imperative::to_string(value)});
            return *this;
        }

        std::string to_string_start() const {
            std::string builder;
M
Megvii Engine Team 已提交
32 33
            builder.append(ssprintf("<%s", name.c_str()));
            for (auto&& [k, v] : attrs) {
34 35 36 37 38 39
                builder.append(ssprintf(" %s=\"%s\"", k.c_str(), v.c_str()));
            }
            builder.append(">\n");
            return builder;
        }

M
Megvii Engine Team 已提交
40
        std::string to_string_end() const { return ssprintf("</%s>\n", name.c_str()); }
41

M
Megvii Engine Team 已提交
42
        ElementGuard(XMLWriter* writer, std::string name) : writer{writer}, name{name} {
43 44 45 46 47 48 49 50
            writer->elements.emplace_back();
        }

        ~ElementGuard() {
            auto children = std::move(writer->elements.back());
            writer->elements.pop_back();
            std::string builder;
            builder.append(to_string_start());
M
Megvii Engine Team 已提交
51
            for (auto&& child : children) {
52 53 54 55 56 57 58
                builder.append(child);
            }
            builder.append(to_string_end());
            writer->elements.back().push_back(builder);
        }
    };
    XMLWriter() {
M
Megvii Engine Team 已提交
59 60
        elements.emplace_back().push_back(
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
61
    }
M
Megvii Engine Team 已提交
62 63
    ElementGuard element(std::string tag) { return ElementGuard{this, tag}; }
    void text(std::string text) { elements.back().push_back(text); }
64 65
    void doctype(std::string element, std::string dtd, std::vector<std::string> args) {
        std::string builder = ssprintf("<!DOCTYPE %s %s", element.c_str(), dtd.c_str());
M
Megvii Engine Team 已提交
66
        for (auto&& arg : args) {
67 68 69 70 71 72 73 74
            builder.append(ssprintf(" %s", arg.c_str()));
        }
        builder.append(">\n");
        elements.back().push_back(builder);
    }
    std::string to_string() const {
        mgb_assert(elements.size() == 1 && elements[0].size() >= 1);
        std::string builder;
M
Megvii Engine Team 已提交
75
        for (auto&& element : elements[0]) {
76 77 78 79 80 81 82 83 84 85
            builder.append(element);
        }
        return builder;
    }
};

struct MemoryChunk {
    std::array<uintptr_t, 2> address;
    std::string name;
    TensorLayout layout;
86 87
    std::array<profiler::Duration, 2> time;
    std::optional<uint64_t> group;
88

M
Megvii Engine Team 已提交
89
    bool empty() const { return address[1] - address[0] == 0; }
90 91 92 93 94 95 96 97
};

struct MemoryFlow {
    std::unordered_map<uint64_t, MemoryChunk> chunks;

    std::pair<uintptr_t, uintptr_t> address_range() const {
        auto addr_begin = std::numeric_limits<uintptr_t>::max();
        auto addr_end = std::numeric_limits<uintptr_t>::min();
M
Megvii Engine Team 已提交
98
        for (auto&& [id, chunk] : chunks) {
99
            MGB_MARK_USED_VAR(id);
M
Megvii Engine Team 已提交
100 101
            if (chunk.empty())
                continue;
102 103 104 105 106 107
            addr_begin = std::min(addr_begin, chunk.address[0]);
            addr_end = std::max(addr_end, chunk.address[1]);
        }
        return {addr_begin, addr_end};
    }

108 109 110
    std::pair<profiler::Duration, profiler::Duration> time_range() const {
        auto time_begin = profiler::Duration::max();
        auto time_end = profiler::Duration::min();
M
Megvii Engine Team 已提交
111
        for (auto&& [id, chunk] : chunks) {
112
            MGB_MARK_USED_VAR(id);
M
Megvii Engine Team 已提交
113 114
            if (chunk.empty())
                continue;
115 116 117 118 119 120 121
            time_begin = std::min(time_begin, chunk.time[0]);
            time_end = std::max(time_end, chunk.time[1]);
        }
        return {time_begin, time_end};
    }

    XMLWriter to_svg() const {
122
        using namespace std::chrono_literals;
123 124 125
        XMLWriter writer;
        auto&& [addr_begin, addr_end] = address_range();
        auto&& [time_begin, time_end] = time_range();
M
Megvii Engine Team 已提交
126 127 128 129
        writer.doctype(
                "svg", "PUBLIC",
                {"\"-//W3C//DTD SVG 1.1//EN\"",
                 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\""});
130 131 132
        auto svg = writer.element("svg");
        svg.attr("xmlns", std::string{"http://www.w3.org/2000/svg"});
        svg.attr("xmlns:tag", std::string{"https://megengine.org.cn"});
133
        auto time_scale = 100us;
134
        double addr_scale = 1e6;
M
Megvii Engine Team 已提交
135 136
        svg.attr("width", (time_end - time_begin) / time_scale);
        svg.attr("height", (addr_end - addr_begin) / addr_scale);
137 138 139 140
        {
            auto rect = writer.element("rect");
            rect.attr("x", 0);
            rect.attr("y", 0);
M
Megvii Engine Team 已提交
141 142
            rect.attr("width", (time_end - time_begin) / time_scale);
            rect.attr("height", (addr_end - addr_begin) / addr_scale);
143 144 145 146
            rect.attr("fill", std::string{"blue"});
        }
        double us = 1e3, ms = 1e6;
        std::map<double, std::string> time2color = {
M
Megvii Engine Team 已提交
147 148 149 150 151 152 153
                {0 * us, "#DDDDDD"},
                {100 * us, "#CCCCCC"},
                {1 * ms, "#BBBBBB"},
                {10 * ms, "#AAAAAA"},
                {100 * ms, "#999999"},
                {1000 * ms, "#888888"},
                {std::numeric_limits<double>::infinity(), "#555555"},
154
        };
M
Megvii Engine Team 已提交
155
        auto time2str = [](profiler::Duration ns) {
156 157
            using pair_t = std::pair<uint64_t, const char*>;
            static pair_t units[] = {
M
Megvii Engine Team 已提交
158 159 160 161
                    {1, "ns "},
                    {1e3, "us "},
                    {1e6, "ms "},
                    {1e9, "s "},
162 163 164 165 166
            };
            std::string builder;
            auto comparator = [](const pair_t& lhs, const pair_t& rhs) {
                return lhs.first < rhs.first;
            };
167
            while (ns.count() > 0) {
M
Megvii Engine Team 已提交
168 169 170 171
                auto iter = std::upper_bound(
                                    std::begin(units), std::end(units),
                                    std::make_pair(ns.count(), ""), comparator) -
                            1;
172
                builder += std::to_string(ns.count() / iter->first) + iter->second;
173 174 175 176
                ns = ns % iter->first;
            }
            return builder;
        };
M
Megvii Engine Team 已提交
177
        auto size2str = [](size_t sz) {
178 179
            using pair_t = std::pair<size_t, const char*>;
            static pair_t units[] = {
M
Megvii Engine Team 已提交
180 181 182 183
                    {1, "B "},
                    {1024, "KB "},
                    {1024 * 1024, "MB "},
                    {1024 * 1024 * 1024, "GB "},
184 185 186 187 188 189
            };
            std::string builder;
            auto comparator = [](const pair_t& lhs, const pair_t& rhs) {
                return lhs.first < rhs.first;
            };
            while (sz > 0) {
M
Megvii Engine Team 已提交
190 191 192 193
                auto iter = std::upper_bound(
                                    std::begin(units), std::end(units),
                                    std::make_pair(sz, ""), comparator) -
                            1;
194 195 196 197 198
                builder += std::to_string(sz / iter->first) + iter->second;
                sz = sz % iter->first;
            }
            return builder;
        };
M
Megvii Engine Team 已提交
199
        for (auto&& [id, chunk] : chunks) {
200
            MGB_MARK_USED_VAR(id);
M
Megvii Engine Team 已提交
201 202 203 204 205 206
            if (chunk.empty())
                continue;
            double left = (chunk.time[0] - time_begin) / time_scale;
            double right = (chunk.time[1] - time_begin) / time_scale;
            double top = (chunk.address[0] - addr_begin) / addr_scale;
            double bottom = (chunk.address[1] - addr_begin) / addr_scale;
207
            double duration = (chunk.time[1] - chunk.time[0]).count();
208 209 210 211 212 213 214 215 216 217 218 219
            {
                auto rect = writer.element("rect");
                rect.attr("x", left);
                rect.attr("y", top);
                rect.attr("height", bottom - top);
                rect.attr("width", right - left);
                rect.attr("fill", time2color.lower_bound(duration)->second);
                auto mge_attr = [&](const char* name, auto&& value) {
                    rect.attr(ssprintf("tag:%s", name), value);
                };
                mge_attr("type", std::string("tensor"));
                mge_attr("name", chunk.name);
M
Megvii Engine Team 已提交
220 221 222
                mge_attr(
                        "address",
                        ssprintf("%p", reinterpret_cast<void*>(chunk.address[0])));
223 224 225 226 227
                mge_attr("size", size2str(chunk.address[1] - chunk.address[0]));
                mge_attr("layout", chunk.layout.to_string());
                mge_attr("produced", time2str(chunk.time[0]));
                mge_attr("erased", time2str(chunk.time[1]));
                mge_attr("duration", time2str(chunk.time[1] - chunk.time[0]));
228 229 230
                if (chunk.group) {
                    mge_attr("group", std::to_string(*chunk.group));
                }
231 232 233 234 235 236
            }
        }
        return writer;
    }
};

M
Megvii Engine Team 已提交
237
struct MemoryFlowVisitor : EventVisitor<MemoryFlowVisitor> {
238
    MemoryFlow memory_flow;
239

240
    template <typename TEvent>
M
Megvii Engine Team 已提交
241
    void visit_event(const TEvent& event) {
242 243 244 245 246 247 248 249 250
        if constexpr (std::is_same_v<TEvent, TensorProduceEvent>) {
            auto& chunk = memory_flow.chunks[event.tensor_id];
            uint64_t address = reinterpret_cast<uintptr_t>(event.ptr);
            auto span = event.layout.span();
            auto dtype = event.layout.dtype;
            // assume dtype is not lowbit
            if (!address) {
                chunk.address = {0, 0};
            } else {
M
Megvii Engine Team 已提交
251 252 253
                chunk.address = {
                        address + span.low_elem * dtype.size(),
                        address + span.high_elem * dtype.size()};
254 255
            }
            chunk.layout = event.layout;
M
Megvii Engine Team 已提交
256 257
            chunk.time[0] =
                    since_start(to_device_time(current->time, current_tensor->device));
258 259 260 261
            chunk.name = current_tensor->name;
            chunk.group = current_tensor->source;
        } else if constexpr (std::is_same_v<TEvent, TensorReleaseEvent>) {
            auto& chunk = memory_flow.chunks[event.tensor_id];
M
Megvii Engine Team 已提交
262 263
            chunk.time[1] =
                    since_start(to_device_time(current->time, current_tensor->device));
264 265 266
        }
    }

267 268 269 270 271
    void notify_counter(std::string key, int64_t old_val, int64_t new_val) {}
};

void dump_memory_flow(std::string filename, Profiler::bundle_t result) {
    MemoryFlowVisitor visitor;
272
    visitor.process_events(result);
273
    debug::write_to_file(filename.c_str(), visitor.memory_flow.to_svg().to_string());
274 275
}

M
Megvii Engine Team 已提交
276
}  // namespace mgb::imperative::profiler