Browse Source

perf(profiling): Remove recursive call in text renderer (#34123)

The function call overhead in the text renderer can be significant with a large
number of frames. This flattens the recursive procedure to use iteration to
avoid the overhead.
Tony Xiao 2 years ago
parent
commit
365bd1d2aa
1 changed files with 12 additions and 12 deletions
  1. 12 12
      static/app/utils/profiling/renderers/textRenderer.tsx

+ 12 - 12
static/app/utils/profiling/renderers/textRenderer.tsx

@@ -69,10 +69,18 @@ class TextRenderer {
       (this.theme.SIZES.BAR_HEIGHT - this.theme.SIZES.BAR_FONT_SIZE / 2) *
       window.devicePixelRatio;
 
-    const drawFrame = (frame: FlamegraphFrame): void => {
+    // We start by iterating over root frames, so we draw the call stacks top-down.
+    // This allows us to do a couple optimizations that improve our best case performance.
+    // 1. We can skip drawing the entire tree if the root frame is not visible
+    // 2. We can skip drawing and
+    const frames: FlamegraphFrame[] = [...this.flamegraph.roots];
+
+    while (frames.length) {
+      const frame = frames.pop()!;
+
       // Check if our rect overlaps with the current viewport and skip it
       if (frame.end < configView.left || frame.start > configView.right) {
-        return;
+        continue;
       }
 
       // We pin the start and end of the frame, so scrolling around keeps text pinned to the left or right side of the viewport
@@ -109,7 +117,7 @@ class TextRenderer {
 
       // Since children of a frame cannot be wider than the frame itself, we can exit early and discard the entire subtree
       if (paddedRectangleWidth <= minWidth) {
-        return;
+        continue;
       }
 
       // We want to draw the text in the vertical center of the frame, so we substract half the height of the text
@@ -132,16 +140,8 @@ class TextRenderer {
       );
 
       for (let i = 0; i < frame.children.length; i++) {
-        drawFrame(frame.children[i]);
+        frames.push(frame.children[i]);
       }
-    };
-
-    // We start by iterating over root frames, so we draw the call stacks top-down.
-    // This allows us to do a couple optimizations that improve our best case performance.
-    // 1. We can skip drawing the entire tree if the root frame is not visible
-    // 2. We can skip drawing and
-    for (let i = 0; i < this.flamegraph.roots.length; i++) {
-      drawFrame(this.flamegraph.roots[i]);
     }
   }
 }