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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions