zoukankan      html  css  js  c++  java
  • VUE组件——FlameGraph火焰图

    先贴个图

    火焰图的应用场景

           用于显示复杂的函数调用链,特点在于:数据量较大。

           详细介绍可以参考阮大师的博客:http://www.ruanyifeng.com/blog/2017/09/flame-graph.html

    本文主要在vue框架的基础上实现函数调用链火焰图,其它框架的实现方式类似,由于应用场景中的数据量较大,使用了三种方式来实现并测试该功能。

    数据源格式:

          函数调用链实质就是一棵树,最下层节点是根;上层节点的位置及宽度依赖于下层节点。

    {
    id: {
    id: 'id_*', //使用字符串id,避免json.parse自动排序
    name: 函数名, percentage: 百分比, parentId: 父节点id, prevId: 兄弟节点id, level: 层级,
    }
    }

      

    计算方式:

         根节点的宽度默认为100%宽度;

        子节点宽度=调用百分比*父节点宽度;

        位置信息x 从父节点的x开始,若前面有兄弟节点prev,则x = prev.x + prev.width;

        位置信息y 从最底端开始,y = level * 16(每层火焰高度为15,间隔1像素)。

    方法一:SVG多组件渲染方式

    将一个节点作为一个组件实现

    <svg width="100%" :height="height">
       <g v-show="!loading" id="graphContainer">
          <call-item v-for="item in items" :item="item" :key = "item.id" @mouseover="displayItem(item)" @mouseout="display=''" 
            :parent="items['id_' + item.parentId]" @click="repaint(item)"
            :prev="items['id_' + item.preId]" :height="cHeight" :cur = "curItem"></call-item>
          <text x="15" :y="cHeight - 15" >{{display}}</text>
        </g>
    </svg>

    mouseover事件,在下册text区域显示鼠标滑动所至节点的详细信息

    click事件,以被点击的节点为最大节点重绘调用链,其父节点均半透明,子节点根据当前节点宽度及位置信息重绘

    单组件渲染方式在节点个数过大时效果较差

    方法二:SVG单组件渲染方式

    使用js拼凑火焰图部分作为模板来渲染,绘制部分和渲染部分如下:

       drawOneCallItem (item, frag, items) {
          let g = '', color = this.calcItemColor(),
            title = `${item.name}, ${item.percentage}%)`, display = title
          display = this.calcDisplay(title, item.width)
          g = `<g class="func_g">
          <title>${title}</title>
          <rect x="${item.pos.x}" y="${item.pos.y}" width="${item.width}" height="15.0" fill="${color}" rx="2" ry="2"
          orig-x="${item.pos.x}" orig-y="${item.pos.y}" orig-width="${item.width}" ></rect>
          <text x="${item.pos.x + 5}" y="${item.pos.y + 12}" >${display}</text>
          </g>`
          frag.push(g)
        },
        initFlameGraph () {
          console.info('starting getData:', new Date().getTime())
          if (this.id > 0) {
            this.depth = 0
            let frag = []
            service.getTree().then(({data}) => {
              let res = data.data
              let graph = document.getElementById('graphContainer')
              graph.innerHTML = ''
              for (let key of Object.keys(res.records)) {
                let item = res.records[key]
                this.drawOneCallItem(item, frag, res.records)
              }
              var MyAppendTo = Vue.extend({
                template: frag.join('')
              })
              let app = new MyAppendTo()
              app.$mount().$appendTo('#graphContainer')
              frag = []
              console.info('draw finish:', new Date().getTime())
            })
          }
        }

    此方案在绘制1万+个节点时加上网络消耗大致需要2秒左右,基本符合需求。

    方法三:Canvas实现方式

    canvas 方式绘制性能非常好,实验过12万+的节点比方法二快3倍左右。但事件处理流程比较复杂,尤其是hover事件的模拟,计算量也比较大。

    计算方式与方法二类似,预先计算好节点位置,然后从最底层按照宽度和位置信息按序绘制即可。

    RoundedRect 类是canvas的单节点绘制类,原本是需要绘制圆矩形的,但宽度低时有问题,后面修改后再更新。
    export default class RoundedRect {
      constructor (ctx, x, y, width, fill, text) {
        this.ctx = ctx
        this.x = x
        this.y = y
        this.width = width
        this.fill = fill
        this.text = text
      }
    
        //
      draw () {
        let ctx = this.ctx
        this.drawRoundedRect(ctx, this.x, this.y, this.width, 15, 0, this.fill, this.text)
      }
    
      calcDisplay (title, width) {
        let cLen = Math.ceil(width / 8) - 2,
          disStr = title
        if (title.length > cLen) {
          disStr = title.substring(0, cLen - 2)
          disStr = disStr.length > 0 ? disStr + '..' : disStr
        }
        return cLen <= 4 ? '' : disStr
      }
    
      drawRoundedRect (ctx, x, y, w, h, r, bgcolor, text) {
        ctx.beginPath()
        ctx.fillStyle = bgcolor
        ctx.fillRect(x, y, w, h)
        ctx.strokeText(this.calcDisplay(text, w), x + 5, y + 12)
        ctx.closePath()
      }
    }

    canvas方案的hover和click事件需要通过鼠标位置信息找到hover或click的是哪个节点,在节点很多时计算量也是相当的大啊,最后不得不放弃canvas方案。选择方法二。

  • 相关阅读:
    HDU 2433 Travel (最短路,BFS,变形)
    HDU 2544 最短路 (最短路,spfa)
    HDU 2063 过山车 (最大匹配,匈牙利算法)
    HDU 1150 Machine Schedule (最小覆盖,匈牙利算法)
    290 Word Pattern 单词模式
    289 Game of Life 生命的游戏
    287 Find the Duplicate Number 寻找重复数
    283 Move Zeroes 移动零
    282 Expression Add Operators 给表达式添加运算符
    279 Perfect Squares 完美平方数
  • 原文地址:https://www.cnblogs.com/yiyitong/p/8182416.html
Copyright © 2011-2022 走看看