如何使用perf_hooks来测量异步操作?

4

下面是一个代码段,描述了foo方法执行的两个异步操作:

const foo = async () => {
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, Math.random() * 100);
  });

  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 100);
  });
};

Promise.all([
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo()
])

我希望测量每个异步操作需要多长时间,然后每个foo执行总共需要多长时间。

我的第一次尝试是:

const { PerformanceObserver, performance } = require('perf_hooks');

const obs = new PerformanceObserver((items) => {
  console.log('performance-observer', items.getEntries());
});

obs.observe({
  entryTypes: ['measure']
});

const foo = async () => {
  performance.mark('A');

  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, Math.random() * 100);
  });

  performance.mark('B');

  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 100);
  });

  performance.mark('C');

  performance.measure('A to B', 'A', 'B');
  performance.measure('A to C', 'A', 'C');
  performance.measure('B to C', 'B', 'C');

  performance.clearMarks();
};

Promise.all([
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo()
]);

然而,这个操作(顾名思义)会抛出一个错误:

(node:29277) UnhandledPromiseRejectionWarning: Error [ERR_INVALID_PERFORMANCE_MARK]: The "B" performance mark has not been set
    at Performance.measure (perf_hooks.js:396:13)
    at foo (/Users/gajus/Documents/dev/temp/test.js:30:15)
    at async Promise.all (index 0)

这是因为PerformanceObserver实例和单个性能标记之间没有关联。

如何使用perf_hooks来测量异步操作?


注意:我想知道每次执行的时间,而不仅仅是所有执行的总和。 - Gajus
1个回答

2

看起来除了在resolve之后标记和测量,没有其他的方法了。

const foo = async () => {
  await new Promise((resolve) => {
    performance.mark('A');

    setTimeout(() => {
      resolve();
      performance.mark('B');
      performance.measure('A to B', 'A', 'B');
    }, Math.random() * 100);
  });

  await new Promise((resolve) => {
    performance.mark('C');
    setTimeout(() => {
      resolve();
      performance.mark('D');
      performance.measure('C to D', 'C', 'D');
    }, 100);
  });

  performance.clearMarks();
};

Promise.all([
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
  foo(),
]);

请注意,在 foo 底部使用 performance.clearMarks() 将全局删除所有标记,因此无论哪个 foo 先解析都会清除所有标记!特别是 C(甚至 A)标记被清除,这将使除第一个 C to D 测量之外的所有测量无效,因为它们将测量程序启动和它们各自的 D 标记之间的时间。我认为在这里做我们想要的正确方法是为每个“纤维”/“线程”/“进程”分配其自己的唯一 ID,并测量 C-${id} 直到 D-${id}。还要注意,然后您可以移动 measure 调用。 - Martin Grönlund

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接