鸿蒙性功能优化之跨线程序列化耗时分析

144 阅读2分钟

一、简介

  使用TaskPool/Worker必然涉及到跨线程传输数据,对象/方法在跨线程传递时需要序列化和反序列化。当对象本身较大且结构复杂时,序列化/反序列化的耗时就会增加,从而影响应用运行的整体性能。DevEco Profiler的Anomaly泳道可以检测序列化和反序列化的耗时,通过ArkTS Callstack泳道定位到序列化/反序列化耗时代码,使用Sendable,提升应用性能。本文会以具体的案例介绍如何使用DevEco Profiler检测序列化和反序列化的耗时。

二、序列化/反序列化性能检测

class Message {
  id: string = '';
  title: string = '';
  content: string = '';

  constructor(id: string, title: string, content: string) {
    this.id = id
    this.title = title
    this.content = content
  }
 }
 
const messages = new Array<Message>()
for (let i = 0; i < 300000; i++) {
  const message = new Message(`消息${i}`, `这是一条点赞消息:${i}`, `张三点赞了你:${i}`)
  messages.push(message)
}
// 将30万条数据传递给子线程
const task = new taskpool.Task(post, messages)
taskpool.execute(task)

@Concurrent
function post(messages: Array<Message>) {
  for (let i = 0; i < messages.length; i++) {
    const msg = messages[i]
    console.log(`${msg.id}${msg.title}${msg.content}`)
  }
}

  如上代码,构建30万条数据,将数据传递给子线程,跨线程传输数据会触发序列化和反序列化。我们使用上面的代码来检测序列化和反序列化的耗时。

  • 打开DevEco Studio,确认设备已连接,待录制应用已安装。
  • 启动应用,点击Profiler,选择Frame模板,选择当前应用进程,点击Create Session。
  • 在录制之前,可以设置序列化/反序列化的阈值,如下图所示。 image.png
  • 开始录制,录制过程中可正常操作应用。

  当Profile分析完成后,就能在Anomaly泳道上看到序列化/反序列化的耗时。 image.png   如下图所示,查看详细信息。 image.png   当发生序列化/反序列化耗时问题,通过下图的步骤就可以定位到耗时代码。 image.png

2、1 修复问题

  为了解决该问题,需要使用@Sendable注解,如下代码,只需在Message类加上@Sendable注解,其它代码不用改。

// 加上@Sendable注解即可
@Sendable
class Message {
id: string = '';
title: string = '';
content: string = '';

constructor(id: string, title: string, content: string) {
  this.id = id
  this.title = title
  this.content = content
}
}

  优化后再次通过序列化超时检测工具检测录制,发现该场景下序列化耗时已小于默认阈值(8ms),在Anomaly泳道中已经没有对应超时的Trace点。跨线程传输数据量较大时,使用Sendable引用传递方式可以有效减少序列化耗时,提升应用性能image.png

  为什么使用Sendable后,跨线程传输数据就不耗时了呢?因为Sendable对象分配在共享堆中,可以在不同并发实例间通过引用传递。通过引用传递方式传输对象相比序列化方式更加高效,同时不会丢失类上携带的成员方法。更多关于Sendable的介绍,可查看鸿蒙多线程并发