Jelajahi Sumber

Re-use profiler visualization for extra networks

catboxanon 1 tahun lalu
induk
melakukan
801461eea2
3 mengubah file dengan 178 tambahan dan 93 penghapusan
  1. 2 0
      .eslintrc.js
  2. 62 0
      javascript/extraNetworks.js
  3. 114 93
      javascript/profilerVisualization.js

+ 2 - 0
.eslintrc.js

@@ -78,6 +78,8 @@ module.exports = {
         //extraNetworks.js
         requestGet: "readonly",
         popup: "readonly",
+        // profilerVisualization.js
+        createVisualizationTable: "readonly",
         // from python
         localization: "readonly",
         // progrssbar.js

+ 62 - 0
javascript/extraNetworks.js

@@ -528,12 +528,74 @@ function popupId(id) {
     popup(storedPopupIds[id]);
 }
 
+function extraNetworksFlattenMetadata(obj) {
+    const result = {};
+
+    // Convert any stringified JSON objects to actual objects
+    for (const key of Object.keys(obj)) {
+        if (typeof obj[key] === 'string') {
+            try {
+                const parsed = JSON.parse(obj[key]);
+                if (parsed && typeof parsed === 'object') {
+                    obj[key] = parsed;
+                }
+            } catch (error) {
+                continue;
+            }
+        }
+    }
+
+    // Flatten the object
+    for (const key of Object.keys(obj)) {
+        if (typeof obj[key] === 'object' && obj[key] !== null) {
+            const nested = extraNetworksFlattenMetadata(obj[key]);
+            for (const nestedKey of Object.keys(nested)) {
+                result[`${key}/${nestedKey}`] = nested[nestedKey];
+            }
+        } else {
+            result[key] = obj[key];
+        }
+    }
+
+    // Special case for handling modelspec keys
+    for (const key of Object.keys(result)) {
+        if (key.startsWith("modelspec.")) {
+            result[key.replaceAll(".", "/")] = result[key];
+            delete result[key];
+        }
+    }
+
+    // Add empty keys to designate hierarchy
+    for (const key of Object.keys(result)) {
+        const parts = key.split("/");
+        for (let i = 1; i < parts.length; i++) {
+            const parent = parts.slice(0, i).join("/");
+            if (!result[parent]) {
+                result[parent] = "";
+            }
+        }
+    }
+
+    return result;
+}
+
 function extraNetworksShowMetadata(text) {
+    try {
+        let parsed = JSON.parse(text);
+        if (parsed && typeof parsed === 'object') {
+            parsed = extraNetworksFlattenMetadata(parsed);
+            const table = createVisualizationTable(parsed, 0);
+            popup(table);
+            return;
+        }
+    } catch (error) { console.debug(error); }
+
     var elem = document.createElement('pre');
     elem.classList.add('popup-metadata');
     elem.textContent = text;
 
     popup(elem);
+    return;
 }
 
 function requestGet(url, data, handler, errorHandler) {

+ 114 - 93
javascript/profilerVisualization.js

@@ -33,120 +33,141 @@ function createRow(table, cellName, items) {
     return res;
 }
 
-function showProfile(path, cutoff = 0.05) {
-    requestGet(path, {}, function(data) {
-        var table = document.createElement('table');
-        table.className = 'popup-table';
-
-        data.records['total'] = data.total;
-        var keys = Object.keys(data.records).sort(function(a, b) {
-            return data.records[b] - data.records[a];
+function createVisualizationTable(data, cutoff = 0, sort = "") {
+    var table = document.createElement('table');
+    table.className = 'popup-table';
+
+    var keys = Object.keys(data);
+    if (sort === "number") {
+        keys = keys.sort(function(a, b) {
+            return data[b] - data[a];
         });
-        var items = keys.map(function(x) {
-            return {key: x, parts: x.split('/'), time: data.records[x]};
+    } else {
+        keys = keys.sort();
+    }
+    var items = keys.map(function(x) {
+        return {key: x, parts: x.split('/'), value: data[x]};
+    });
+    var maxLength = items.reduce(function(a, b) {
+        return Math.max(a, b.parts.length);
+    }, 0);
+
+    var cols = createRow(
+        table,
+        'th',
+        [
+            cutoff === 0 ? 'key' : 'record',
+            cutoff === 0 ? 'value' : 'seconds'
+        ]
+    );
+    cols[0].colSpan = maxLength;
+
+    function arraysEqual(a, b) {
+        return !(a < b || b < a);
+    }
+
+    var addLevel = function(level, parent, hide) {
+        var matching = items.filter(function(x) {
+            return x.parts[level] && !x.parts[level + 1] && arraysEqual(x.parts.slice(0, level), parent);
         });
-        var maxLength = items.reduce(function(a, b) {
-            return Math.max(a, b.parts.length);
-        }, 0);
-
-        var cols = createRow(table, 'th', ['record', 'seconds']);
-        cols[0].colSpan = maxLength;
-
-        function arraysEqual(a, b) {
-            return !(a < b || b < a);
+        if (sort === "number") {
+            matching = matching.sort(function(a, b) {
+                return b.value - a.value;
+            });
+        } else {
+            matching = matching.sort();
         }
+        var othersTime = 0;
+        var othersList = [];
+        var othersRows = [];
+        var childrenRows = [];
+        matching.forEach(function(x) {
+            var visible = (cutoff === 0 && !hide) || (x.value >= cutoff && !hide);
+
+            var cells = [];
+            for (var i = 0; i < maxLength; i++) {
+                cells.push(x.parts[i]);
+            }
+            cells.push(cutoff === 0 ? x.value : x.value.toFixed(3));
+            var cols = createRow(table, 'td', cells);
+            for (i = 0; i < level; i++) {
+                cols[i].className = 'muted';
+            }
 
-        var addLevel = function(level, parent, hide) {
-            var matching = items.filter(function(x) {
-                return x.parts[level] && !x.parts[level + 1] && arraysEqual(x.parts.slice(0, level), parent);
-            });
-            var sorted = matching.sort(function(a, b) {
-                return b.time - a.time;
-            });
-            var othersTime = 0;
-            var othersList = [];
-            var othersRows = [];
-            var childrenRows = [];
-            sorted.forEach(function(x) {
-                var visible = x.time >= cutoff && !hide;
-
-                var cells = [];
-                for (var i = 0; i < maxLength; i++) {
-                    cells.push(x.parts[i]);
-                }
-                cells.push(x.time.toFixed(3));
-                var cols = createRow(table, 'td', cells);
-                for (i = 0; i < level; i++) {
-                    cols[i].className = 'muted';
-                }
-
-                var tr = cols[0].parentNode;
-                if (!visible) {
-                    tr.classList.add("hidden");
-                }
-
-                if (x.time >= cutoff) {
-                    childrenRows.push(tr);
-                } else {
-                    othersTime += x.time;
-                    othersList.push(x.parts[level]);
-                    othersRows.push(tr);
-                }
-
-                var children = addLevel(level + 1, parent.concat([x.parts[level]]), true);
-                if (children.length > 0) {
-                    var cell = cols[level];
-                    var onclick = function() {
-                        cell.classList.remove("link");
-                        cell.removeEventListener("click", onclick);
-                        children.forEach(function(x) {
-                            x.classList.remove("hidden");
-                        });
-                    };
-                    cell.classList.add("link");
-                    cell.addEventListener("click", onclick);
-                }
-            });
+            var tr = cols[0].parentNode;
+            if (!visible) {
+                tr.classList.add("hidden");
+            }
 
-            if (othersTime > 0) {
-                var cells = [];
-                for (var i = 0; i < maxLength; i++) {
-                    cells.push(parent[i]);
-                }
-                cells.push(othersTime.toFixed(3));
-                cells[level] = 'others';
-                var cols = createRow(table, 'td', cells);
-                for (i = 0; i < level; i++) {
-                    cols[i].className = 'muted';
-                }
+            if (cutoff === 0 || x.value >= cutoff) {
+                childrenRows.push(tr);
+            } else {
+                othersTime += x.value;
+                othersList.push(x.parts[level]);
+                othersRows.push(tr);
+            }
 
+            var children = addLevel(level + 1, parent.concat([x.parts[level]]), true);
+            if (children.length > 0) {
                 var cell = cols[level];
-                var tr = cell.parentNode;
                 var onclick = function() {
-                    tr.classList.add("hidden");
                     cell.classList.remove("link");
                     cell.removeEventListener("click", onclick);
-                    othersRows.forEach(function(x) {
+                    children.forEach(function(x) {
                         x.classList.remove("hidden");
                     });
                 };
-
-                cell.title = othersList.join(", ");
                 cell.classList.add("link");
                 cell.addEventListener("click", onclick);
+            }
+        });
 
-                if (hide) {
-                    tr.classList.add("hidden");
-                }
+        if (othersTime > 0) {
+            var cells = [];
+            for (var i = 0; i < maxLength; i++) {
+                cells.push(parent[i]);
+            }
+            cells.push(othersTime.toFixed(3));
+            cells[level] = 'others';
+            var cols = createRow(table, 'td', cells);
+            for (i = 0; i < level; i++) {
+                cols[i].className = 'muted';
+            }
 
-                childrenRows.push(tr);
+            var cell = cols[level];
+            var tr = cell.parentNode;
+            var onclick = function() {
+                tr.classList.add("hidden");
+                cell.classList.remove("link");
+                cell.removeEventListener("click", onclick);
+                othersRows.forEach(function(x) {
+                    x.classList.remove("hidden");
+                });
+            };
+
+            cell.title = othersList.join(", ");
+            cell.classList.add("link");
+            cell.addEventListener("click", onclick);
+
+            if (hide) {
+                tr.classList.add("hidden");
             }
 
-            return childrenRows;
-        };
+            childrenRows.push(tr);
+        }
+
+        return childrenRows;
+    };
 
-        addLevel(0, []);
+    addLevel(0, []);
+
+    return table;
+}
 
+function showProfile(path, cutoff = 0.05) {
+    requestGet(path, {}, function(data) {
+        data.records['total'] = data.total;
+        const table = createVisualizationTable(data.records, cutoff, "number");
         popup(table);
     });
 }