GitOrigin-RevId: f0eab26398
tags/v1.5.0
| @@ -1,3 +1,11 @@ | |||
| #! /usr/bin/env python3 | |||
| # MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
| # | |||
| # Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, | |||
| # software distributed under the License is distributed on an | |||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| import argparse | |||
| import getopt | |||
| import os | |||
| @@ -18,6 +18,8 @@ | |||
| var svg = undefined; | |||
| var svgWidth = undefined; | |||
| var svgHeight = undefined; | |||
| var resetBtn = document.getElementById('resetBtn'); | |||
| var relocBtn = document.getElementById('relocBtn'); | |||
| var loadDesc = (svgElem) => { | |||
| var mgeType = svgElem.attributes['tag:type']; | |||
| @@ -52,7 +54,7 @@ | |||
| lastColor = undefined; | |||
| }; | |||
| function recLoadSVG(svgElem) { | |||
| var recLoadSVG = svgElem => { | |||
| if (svgElem.children === undefined) { | |||
| return; | |||
| } | |||
| @@ -71,8 +73,37 @@ | |||
| recLoadSVG(child); | |||
| } | |||
| } | |||
| var scaleBoard = (x, y) => { | |||
| var transform = 'scale(' + x + ',' + y + ')'; | |||
| svg.setAttribute('transform', transform); | |||
| board.style['width'] = svgWidth * x; | |||
| board.style['height'] = svgHeight * y; | |||
| } | |||
| var autoScaleBoard = () => { | |||
| var hRangeValue = Math.sqrt(Number(hRange.value) / 10); | |||
| var vRangeValue = Math.sqrt(Number(vRange.value) / 10); | |||
| scaleBoard(Number(hRangeValue), Number(vRangeValue)); | |||
| } | |||
| var zoomBoard = dScale => { | |||
| scale *= dScale; | |||
| scaleBoard(scale, scale); | |||
| }; | |||
| function loadSVG() { | |||
| var reset = () => { | |||
| if (!svgHeight || !svgWidth) { | |||
| return; | |||
| } | |||
| var vScale = window.screen.availHeight / svgHeight; | |||
| var hScale = window.screen.availWidth / svgWidth; | |||
| var minScale = Math.min(hScale, vScale); | |||
| zoomBoard(minScale / scale); | |||
| window.scrollTo({ | |||
| top: 0, | |||
| left: 0, | |||
| }); | |||
| }; | |||
| var loadSVG = () => { | |||
| var file = fileInput.files[0]; | |||
| var reader = new FileReader(); | |||
| reader.readAsText(file, "UTF-8"); | |||
| @@ -98,26 +129,10 @@ | |||
| info.innerHTML = elemList.join(''); | |||
| } | |||
| } | |||
| setTimeout(reset, 1); | |||
| }; | |||
| } | |||
| function scaleBoard(x, y) { | |||
| var transform = 'scale(' + x + ',' + y + ')'; | |||
| svg.setAttribute('transform', transform); | |||
| board.style['width'] = svgWidth * x; | |||
| board.style['height'] = svgHeight * y; | |||
| } | |||
| function autoScaleBoard() { | |||
| var hRangeValue = Math.sqrt(Number(hRange.value) / 10); | |||
| var vRangeValue = Math.sqrt(Number(vRange.value) / 10); | |||
| scaleBoard(Number(hRangeValue), Number(vRangeValue)); | |||
| } | |||
| fileInput.onchange = loadSVG; | |||
| var zoomBoard = dScale => { | |||
| scale *= dScale; | |||
| scaleBoard(scale, scale); | |||
| }; | |||
| window.addEventListener('wheel', e => { | |||
| console.log(e); | |||
| if (e.ctrlKey) { | |||
| e.preventDefault(); | |||
| e.stopPropagation(); | |||
| @@ -136,19 +151,39 @@ | |||
| top: y, | |||
| left: x, | |||
| }); | |||
| console.log('scroll', [x, y]); | |||
| } else if (e.altKey) { | |||
| } | |||
| }, { 'passive': false }); | |||
| fileInput.onchange = loadSVG; | |||
| resetBtn.onclick = reset; | |||
| relocBtn.onclick = () => { | |||
| if (!lastElem) { | |||
| return; | |||
| } | |||
| y = scale * lastElem.attributes['y'].value; | |||
| x = scale * lastElem.attributes['x'].value; | |||
| window.scrollTo({ | |||
| top: y - window.screen.availHeight/2, | |||
| left: x - window.screen.availWidth/2, | |||
| behavior: 'smooth', | |||
| }); | |||
| }; | |||
| }; | |||
| </script> | |||
| <body> | |||
| <p id="desc" style="position: fixed;bottom: 0; background-color: white;">desc</p> | |||
| <p id="info" style="position: fixed;top: 0; right: 0; background-color: white;">info</p> | |||
| <p id="board" | |||
| style="white-space: nowrap; display: flex; justify-content: center; align-content: center; align-items: center; margin: 0;opacity: 0.7;"> | |||
| style="white-space: nowrap; display: flex; justify-content: center; align-content: center; align-items: center; margin: 0; left: 0;"> | |||
| </p> | |||
| <input type='file' id='fileInput' style="position: fixed; top: 0; background-color: white;"></input> | |||
| <div style="display: flex; position: fixed; top: 0; left: 0; right: 0; background-color: white; flex-grow: 2;"> | |||
| <input type='file' id='fileInput'></input> | |||
| <div style="flex-grow: 1;"></div> | |||
| <button id='resetBtn'>reset</button> | |||
| <button id='relocBtn'>reloc</button> | |||
| </div> | |||
| <p id="desc" style="position: fixed; bottom: 0; background-color: white;">desc</p> | |||
| <p id="info" style="position: fixed;top: 0; right: 0; background-color: white;">info</p> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,61 @@ | |||
| #! /usr/bin/env python3 | |||
| # MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
| # | |||
| # Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, | |||
| # software distributed under the License is distributed on an | |||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| import argparse | |||
| import contextlib | |||
| import getopt | |||
| import http.server | |||
| import os | |||
| import runpy | |||
| import sys | |||
| import tempfile | |||
| from megengine.logger import get_logger | |||
| def main(): | |||
| parser = argparse.ArgumentParser( | |||
| prog="megengine.tools.svg_viewer", | |||
| description="View SVG Graph produced bt megengine profiler", | |||
| ) | |||
| parser.add_argument("-p", "--port", type=int, default=8000, help="server port") | |||
| parser.add_argument( | |||
| "-a", "--address", type=str, default="localhost", help="server address" | |||
| ) | |||
| args = parser.parse_args() | |||
| address = args.address | |||
| port = args.port | |||
| src_filename = "svg_viewer.html" | |||
| dst_filename = "index.html" | |||
| src_path = os.path.join(os.path.dirname(__file__), src_filename) | |||
| url = "http://{}:{}/{}".format("localhost", port, dst_filename) | |||
| ssh_fwd_cmd = "ssh -L {}:localhost:{} <remote ip>".format(port, port) | |||
| with tempfile.TemporaryDirectory() as serve_dir: | |||
| dst_path = os.path.join(serve_dir, dst_filename) | |||
| os.symlink(src_path, dst_path) | |||
| os.chdir(serve_dir) | |||
| get_logger().info("cd to serve directory: {}, starting".format(serve_dir)) | |||
| server = http.server.HTTPServer( | |||
| (address, port), http.server.SimpleHTTPRequestHandler | |||
| ) | |||
| get_logger().info( | |||
| "server started, please visit '{}' to watch profiling result".format(url) | |||
| ) | |||
| get_logger().info( | |||
| "if you are in remote environment, use '{}' to forward port to local".format( | |||
| ssh_fwd_cmd | |||
| ) | |||
| ) | |||
| try: | |||
| server.serve_forever() | |||
| except KeyboardInterrupt: | |||
| get_logger().info("server exiting") | |||
| if __name__ == "__main__": | |||
| main() | |||
| @@ -81,6 +81,7 @@ class Profiler(ContextDecorator): | |||
| for opt, optval in Profiler.valid_options.items(): | |||
| self._options[opt] = int(kwargs.pop(opt, optval)) | |||
| self._pid = "<PID>" | |||
| self._dump_callback = None | |||
| @property | |||
| def path(self): | |||
| @@ -48,7 +48,11 @@ namespace mgb { | |||
| using namespace profiler; | |||
| } | |||
| #ifdef __GNUG__ | |||
| #if defined(_WIN32) || defined(_WIN64) | |||
| #define SYMBOL_EXPORT __declspec(dllexport) | |||
| #else | |||
| #define SYMBOL_EXPORT __attribute__((visibility("default"))) | |||
| #endif | |||
| namespace mgb { | |||
| @@ -62,17 +66,17 @@ namespace mgb { | |||
| * mgb::imperative_log_profile("MY MESSAGE"); | |||
| * | |||
| **/ | |||
| __attribute__((visibility("default"))) | |||
| SYMBOL_EXPORT | |||
| void imperative_log_profile_begin(const char* message) { | |||
| RECORD_EVENT(CustomEvent, std::string{message}); | |||
| } | |||
| __attribute__((visibility("default"))) | |||
| SYMBOL_EXPORT | |||
| void imperative_log_profile_end(const char* message) { | |||
| RECORD_EVENT(CustomFinishEvent, std::string{message}); | |||
| } | |||
| __attribute__((visibility("default"))) | |||
| SYMBOL_EXPORT | |||
| void imperative_log_profile(const char* message){ | |||
| imperative_log_profile_begin(message); | |||
| imperative_log_profile_end(message); | |||
| @@ -80,8 +84,6 @@ void imperative_log_profile(const char* message){ | |||
| } | |||
| #endif | |||
| std::thread::id ChannelImpl::get_worker_tid() { | |||
| return m_worker_state.tid; | |||
| } | |||
| @@ -73,6 +73,8 @@ void Profiler::dump_profile(std::string basename, std::string format, results_t | |||
| auto thread_dict = get_thread_dict(); | |||
| if (format == "chrome_timeline.json") { | |||
| profiler::dump_chrome_timeline(basename, options, thread_dict, results); | |||
| } else if (format == "memory_flow.svg") { | |||
| profiler::dump_memory_flow(basename, options, thread_dict, results); | |||
| } else { | |||
| mgb_log_error("unsupported profiling format %s", format.c_str()); | |||
| } | |||
| @@ -161,12 +161,10 @@ public: | |||
| event_list->add(event.to_json()); | |||
| } | |||
| (*result)["traceEvents"] = event_list; | |||
| //(*result)["localTime"] = json::String::make(std::to_string((double)m_local_time/1e3)); | |||
| return result; | |||
| } | |||
| private: | |||
| std::vector<ChromeTraceEvent> m_content; | |||
| uint64_t m_local_time; | |||
| }; | |||
| @@ -408,9 +406,7 @@ void dump_chrome_timeline(std::string filename, Profiler::options_t options, Pro | |||
| }); | |||
| HANDLE_EVENT(TensorWaitPropEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id]; | |||
| NEW_HOST("TensorWaitProp", 'B'); | |||
| //.args(TENSOR_PROPS); | |||
| if (event.prop == TensorProp::HostValue) { | |||
| INC_COUNTER(wait_value_count, 1); | |||
| } else if (event.prop == TensorProp::Shape) { | |||
| @@ -433,7 +429,6 @@ void dump_chrome_timeline(std::string filename, Profiler::options_t options, Pro | |||
| }); | |||
| HANDLE_EVENT(TensorNotifyPropEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id]; | |||
| NEW_HOST(ssprintf("%d", pid), 's') | |||
| .id(event.tensor_id) | |||
| .cat("TensorProp") | |||
| @@ -471,9 +466,7 @@ void dump_chrome_timeline(std::string filename, Profiler::options_t options, Pro | |||
| }); | |||
| HANDLE_EVENT(TensorCommandEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id]; | |||
| NEW_HOST(ssprintf("%s %zu", TENSOR_COMMAND_KIND, event.tensor_id), 'B'); | |||
| //.args(TENSOR_PROPS); | |||
| }); | |||
| HANDLE_EVENT(TensorCommandFinishEvent, { | |||
| @@ -19,4 +19,6 @@ namespace mgb::imperative::profiler { | |||
| void dump_chrome_timeline(std::string filename, Profiler::options_t options, Profiler::thread_dict_t thread_dict, Profiler::results_t results); | |||
| void dump_memory_flow(std::string filename, Profiler::options_t options, Profiler::thread_dict_t thread_dict, Profiler::results_t results); | |||
| } | |||
| @@ -0,0 +1,306 @@ | |||
| #include <map> | |||
| #include <vector> | |||
| #include <array> | |||
| #include "megbrain/imperative/utils/to_string.h" | |||
| #include "megbrain/utils/debug.h" | |||
| #include "./formats.h" | |||
| #include "./states.h" | |||
| #include "./events.h" | |||
| namespace mgb::imperative::profiler { | |||
| class XMLWriter { | |||
| private: | |||
| std::vector<std::vector<std::string>> elements; | |||
| 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; | |||
| builder.append(ssprintf("<%s", | |||
| name.c_str())); | |||
| for (auto&& [k, v]: attrs) { | |||
| builder.append(ssprintf(" %s=\"%s\"", k.c_str(), v.c_str())); | |||
| } | |||
| builder.append(">\n"); | |||
| return builder; | |||
| } | |||
| std::string to_string_end() const { | |||
| return ssprintf("</%s>\n", name.c_str()); | |||
| } | |||
| ElementGuard(XMLWriter* writer, std::string name): writer{writer}, name{name} { | |||
| writer->elements.emplace_back(); | |||
| } | |||
| ~ElementGuard() { | |||
| auto children = std::move(writer->elements.back()); | |||
| writer->elements.pop_back(); | |||
| std::string builder; | |||
| builder.append(to_string_start()); | |||
| for (auto&& child: children) { | |||
| builder.append(child); | |||
| } | |||
| builder.append(to_string_end()); | |||
| writer->elements.back().push_back(builder); | |||
| } | |||
| }; | |||
| XMLWriter() { | |||
| elements.emplace_back().push_back("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | |||
| } | |||
| ElementGuard element(std::string tag) { | |||
| return ElementGuard{this, tag}; | |||
| } | |||
| void text(std::string text) { | |||
| elements.back().push_back(text); | |||
| } | |||
| 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()); | |||
| for (auto&& arg: args) { | |||
| 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; | |||
| for (auto&& element: elements[0]) { | |||
| builder.append(element); | |||
| } | |||
| return builder; | |||
| } | |||
| }; | |||
| struct MemoryChunk { | |||
| std::array<uintptr_t, 2> address; | |||
| std::string name; | |||
| TensorLayout layout; | |||
| std::array<uint64_t, 2> time; | |||
| bool empty() const { | |||
| return address[1] - address[0] == 0; | |||
| } | |||
| }; | |||
| 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(); | |||
| for(auto&& [id, chunk]: chunks) { | |||
| if (chunk.empty()) continue; | |||
| addr_begin = std::min(addr_begin, chunk.address[0]); | |||
| addr_end = std::max(addr_end, chunk.address[1]); | |||
| } | |||
| return {addr_begin, addr_end}; | |||
| } | |||
| std::pair<uint64_t, uint64_t> time_range() const { | |||
| auto time_begin = std::numeric_limits<uint64_t>::max(); | |||
| auto time_end = std::numeric_limits<uint64_t>::min(); | |||
| for(auto&& [id, chunk]: chunks) { | |||
| if (chunk.empty()) continue; | |||
| time_begin = std::min(time_begin, chunk.time[0]); | |||
| time_end = std::max(time_end, chunk.time[1]); | |||
| } | |||
| return {time_begin, time_end}; | |||
| } | |||
| std::shared_ptr<json::Array> to_json() const { | |||
| auto results = json::Array::make(); | |||
| for(auto&& [id, chunk]: chunks) { | |||
| if (chunk.empty()) continue; | |||
| auto address = json::Array::make(); | |||
| auto time = json::Array::make(); | |||
| address->add(json::String::make(std::to_string(chunk.address[0]))); | |||
| address->add(json::String::make(std::to_string(chunk.address[1]))); | |||
| time->add(json::String::make(std::to_string(chunk.time[0]))); | |||
| time->add(json::String::make(std::to_string(chunk.time[1]))); | |||
| results->add(json::Object::make({ | |||
| {"address", address}, | |||
| {"name", json::String::make(chunk.name)}, | |||
| {"layout", json::String::make(chunk.layout.to_string())}, | |||
| {"time", time} | |||
| })); | |||
| } | |||
| return results; | |||
| } | |||
| XMLWriter to_svg() const { | |||
| XMLWriter writer; | |||
| auto&& [addr_begin, addr_end] = address_range(); | |||
| auto&& [time_begin, time_end] = time_range(); | |||
| writer.doctype("svg", "PUBLIC", { | |||
| "\"-//W3C//DTD SVG 1.1//EN\"", | |||
| "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"" | |||
| }); | |||
| 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"}); | |||
| double time_scale = 1e5; | |||
| double addr_scale = 1e6; | |||
| svg.attr("width", (time_end-time_begin)/time_scale); | |||
| svg.attr("height", (addr_end-addr_begin)/addr_scale); | |||
| { | |||
| auto rect = writer.element("rect"); | |||
| rect.attr("x", 0); | |||
| rect.attr("y", 0); | |||
| rect.attr("width", (time_end-time_begin)/time_scale); | |||
| rect.attr("height", (addr_end-addr_begin)/addr_scale); | |||
| rect.attr("fill", std::string{"blue"}); | |||
| } | |||
| double us = 1e3, ms = 1e6; | |||
| std::map<double, std::string> time2color = { | |||
| {0 * us, "#DDDDDD"}, | |||
| {100 * us, "#CCCCCC"}, | |||
| {1 * ms, "#BBBBBB"}, | |||
| {10 * ms, "#AAAAAA"}, | |||
| {100 * ms, "#999999"}, | |||
| {1000 * ms, "#888888"}, | |||
| {std::numeric_limits<double>::infinity(), "#555555"}, | |||
| }; | |||
| auto time2str = [](uint64_t ns){ | |||
| using pair_t = std::pair<uint64_t, const char*>; | |||
| static pair_t units[] = { | |||
| {1, "ns "}, | |||
| {1e3, "us "}, | |||
| {1e6, "ms "}, | |||
| {1e9, "s "}, | |||
| }; | |||
| std::string builder; | |||
| auto comparator = [](const pair_t& lhs, const pair_t& rhs) { | |||
| return lhs.first < rhs.first; | |||
| }; | |||
| while (ns > 0) { | |||
| auto iter = std::upper_bound(std::begin(units), std::end(units), std::make_pair(ns, ""), comparator) - 1; | |||
| builder += std::to_string(ns / iter->first) + iter->second; | |||
| ns = ns % iter->first; | |||
| } | |||
| return builder; | |||
| }; | |||
| auto size2str = [](size_t sz){ | |||
| using pair_t = std::pair<size_t, const char*>; | |||
| static pair_t units[] = { | |||
| {1, "B "}, | |||
| {1024, "KB "}, | |||
| {1024*1024, "MB "}, | |||
| {1024*1024*1024, "GB "}, | |||
| }; | |||
| std::string builder; | |||
| auto comparator = [](const pair_t& lhs, const pair_t& rhs) { | |||
| return lhs.first < rhs.first; | |||
| }; | |||
| while (sz > 0) { | |||
| auto iter = std::upper_bound(std::begin(units), std::end(units), std::make_pair(sz, ""), comparator) - 1; | |||
| builder += std::to_string(sz / iter->first) + iter->second; | |||
| sz = sz % iter->first; | |||
| } | |||
| return builder; | |||
| }; | |||
| for (auto&& [id, chunk]: chunks) { | |||
| 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; | |||
| double duration = chunk.time[1] - chunk.time[0]; | |||
| { | |||
| 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); | |||
| mge_attr("address", ssprintf("%p", reinterpret_cast<void*>(chunk.address[0]))); | |||
| 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])); | |||
| } | |||
| } | |||
| return writer; | |||
| } | |||
| }; | |||
| void dump_memory_flow(std::string filename, Profiler::options_t options, Profiler::thread_dict_t thread_dict, Profiler::results_t results) { | |||
| MemoryFlow flow; | |||
| ProfileDataCollector collector; | |||
| ProfileState state; | |||
| #define HANDLE_EVENT(type, ...) \ | |||
| collector.handle<type>([&](uint64_t id, std::thread::id tid, uint64_t time, type event) __VA_ARGS__ ); | |||
| HANDLE_EVENT(TensorDeclareEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id] = {}; | |||
| tensor_state.id = event.tensor_id; | |||
| tensor_state.name = event.name; | |||
| }); | |||
| HANDLE_EVENT(TensorProduceEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id]; | |||
| tensor_state.device = event.device; | |||
| tensor_state.layout = event.layout; | |||
| tensor_state.produced = time; | |||
| state.tensors_by_size.insert({tensor_state.id, tensor_state.size_in_bytes()}); | |||
| state.tensors_by_produced.insert({tensor_state.id, tensor_state.produced}); | |||
| auto& chunk = flow.chunks[event.tensor_id]; | |||
| uintptr_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 { | |||
| chunk.address = {address+span.low_elem*dtype.size(), address+span.high_elem*dtype.size()}; | |||
| } | |||
| chunk.layout = tensor_state.layout; | |||
| chunk.time[0] = time; | |||
| chunk.name = tensor_state.name; | |||
| }); | |||
| HANDLE_EVENT(TensorReleaseEvent, { | |||
| auto& tensor_state = state.tensors[event.tensor_id]; | |||
| state.tensors_by_size.erase({tensor_state.id, tensor_state.size_in_bytes()}); | |||
| state.tensors_by_produced.erase({tensor_state.id, tensor_state.produced}); | |||
| auto& chunk = flow.chunks[event.tensor_id]; | |||
| chunk.time[1] = time; | |||
| }); | |||
| HANDLE_EVENT(ScopeEvent, { | |||
| state.threads[tid].scope_stack.push_back(event.name); | |||
| }); | |||
| HANDLE_EVENT(ScopeFinishEvent, { | |||
| mgb_assert(state.threads[tid].scope_stack.back() == event.name); | |||
| state.threads[tid].scope_stack.pop_back(); | |||
| }); | |||
| for (auto&& result: results) { | |||
| collector(result.second.id, result.first, result.second.time, result.second.data); | |||
| } | |||
| debug::write_to_file(filename.c_str(), flow.to_svg().to_string()); | |||
| } | |||
| } | |||