Skip to content

🤔 [QUESTION] Line 图在120HZ更新频率下EE事件积压. #3871

@shiwanmenghuxiahuashan

Description

@shiwanmenghuxiahuashan

🐛 使用Line图表,以120hz频率更新图表数据,EE事件积压.

G2 的Annotation组件调用 update 方法时,调用onAfterRender方法,会调用ee.once方法,但是好像因为更新频率过快,事件没有触发,造成事件积压,内存上升.

Image Image

以下代码可以复现:

// #hook.ts 
import { ref, onUnmounted } from 'vue';
import { Line } from '@antv/g2plot';

console.dir(Line.__proto__.prototype.__proto__);
Line.__proto__.prototype.__proto__.on = function (evt, callback, once) {
  //   if (evt === 'afterrender') {
  //     return;
  //   }
  if (!this._events[evt]) {
    this._events[evt] = [];
  }
  this._events[evt].push({
    callback,
    once: !!once
  });
  if (evt === 'afterrender') {
    if (this._events[evt].length % 1000 == 0) {
      console.log(this._events, this);
    }
  }
  return this;
};

// -------------------------
const MAX_DATA_POINTS = 120;
const RENDER_FPS = 30;
const RENDER_INTERVAL = 1000 / RENDER_FPS;

const useWatchChart = (options) => {
  const chartContainerRef = ref(null);
  let chart = null;
  let isMounted = false;
  let animationFrameId = null;

  let dataBuffer = [];
  let pendingDataQueue = [];

  // 模拟数据生成器
  let mockDataIndex = 0;

  function mountChart() {
    if (!chartContainerRef.value) return;

    isMounted = true;

    chart = new Line(chartContainerRef.value, {
      data: dataBuffer,
      xField: 'index',
      yField: 'value',
      seriesField: 'type',
      color: ['green'],
      animation: false,
      smooth: false,
      tooltip: false,
      xAxis: {
        min: 0,
        max: MAX_DATA_POINTS,
        tickCount: 7
      },
      yAxis: {
        min: -0.01,
        max: 1
      }
    });

    console.log(chart);

    chart.render();
    startRenderLoop();
  }

  function unmountChart() {
    isMounted = false;

    if (animationFrameId) {
      cancelAnimationFrame(animationFrameId);
      animationFrameId = null;
    }

    if (chart) {
      chart.destroy();
      chart = null;
    }

    dataBuffer = [];
    pendingDataQueue = [];

    console.log('Chart unmounted');
  }

  // 模拟Socket数据接收
  function updateDataBuffer() {
    if (!isMounted) return;

    // 生成模拟数据
    const mockData = {
      value1: { value: Math.random() },
      value2: { value: Math.random() }
    };

    // 模拟多字段数据推送
    ['value1', 'value2'].forEach((key) => {
      pendingDataQueue.push({
        type: key,
        value: mockData[key].value,
        index: 0
      });
    });
    // // 每处理1000个数据点记录一次
    // if (mockDataIndex % 1000 === 0) {
    //   if (chart) {
    //     chart.logEvent();
    //   }
    // }
    mockDataIndex++;
  }

  let lastRenderTime = 0;
  const renderLoop = (currentTime) => {
    if (!isMounted) return;

    animationFrameId = requestAnimationFrame(renderLoop);

    // 限制帧率
    if (currentTime - lastRenderTime < RENDER_INTERVAL) return;
    if (pendingDataQueue.length === 0) return;

    lastRenderTime = currentTime;

    // 消费队列
    for (let i = 0; i < pendingDataQueue.length; i++) {
      dataBuffer.push(pendingDataQueue[i]);
    }
    pendingDataQueue.length = 0;

    // 限制缓冲区大小
    const maxTotalItems = MAX_DATA_POINTS * 2; // 2个字段
    if (dataBuffer.length > maxTotalItems) {
      const overflow = dataBuffer.length - maxTotalItems;
      const deleteCount = Math.ceil(overflow / 2) * 2;
      dataBuffer.splice(0, deleteCount);
    }

    // 更新索引
    for (let i = 0; i < dataBuffer.length; i++) {
      dataBuffer[i].index = Math.floor(i / 2);
    }

    // 更新图表
    if (chart) {
      chart.changeData(dataBuffer);
    }
  };

  function startRenderLoop() {
    if (!animationFrameId) {
      animationFrameId = requestAnimationFrame(renderLoop);
    }
  }

  // 模拟高频数据推送
  function startMockDataStream() {
    console.log('Starting mock data stream...');

    const intervalId = setInterval(() => {
      if (isMounted) {
        updateDataBuffer();
      }
    }, 1000 / 120); // 模拟120Hz数据

    return () => {
      clearInterval(intervalId);
      console.log('Mock data stream stopped');
    };
  }

  onUnmounted(() => {
    unmountChart();
  });

  return {
    chartContainerRef,
    mountChart,
    updateDataBuffer,
    unmountChart,
    startMockDataStream,
    isMounted
  };
};

export { useWatchChart };

// 在Vue组件中使用
import { useWatchChart } from './hooks';

const { mountChart, startMockDataStream, unmountChart, chartContainerRef } =
  useWatchChart({});
let stopMockStream = null;

const remove = () => {
  if (stopMockStream) stopMockStream();
  unmountChart();
};
const start = () => {
  mountChart();
  // 开始模拟数据流
  stopMockStream = startMockDataStream();
};
</script>

<template>
  <div class="io-demo-page-wrapper">
    <div class="io-demo-hero-wrapper">
      <div
        class="chartContainer"
        ref="chartContainerRef"></div>
      <el-button @click="remove">remove</el-button>
      <el-button @click="start">start</el-button>
    </div>
  </div>
</template>

<style lang="scss">
.io-demo-page-wrapper {
  height: 100%;
  .chartContainer {
    width: 600px;
    height: 300px;
  }
}
</style>

我不确定是什么问题,但是事件累计造成了内存上升,对于10个图表如上频率更新,持续较长时间,内存上升明显.

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions