Flutter 性能优化的利器 —— Tracing-阿里云开发者社区

开发者社区> 新零售淘系技术> 正文

Flutter 性能优化的利器 —— Tracing

简介: 对于任何技术栈,都会有一个绕不过去的坎,那就是性能优化,而对于如何进行性能优化,最重要的前提就是需要知道具体的耗时分布,要知道耗时分布,就得打点(时间戳),一般的性能打点都是一些散点,比较凌乱,而本文要讲的 Tracing 则是性能打点的一种非常优雅的实现,它以瀑布流的形式呈现,非常直观,还有一个更直观的名字叫 火焰图 Tracing 顾名思义 —— 追踪每段耗时分布。

滚动.gif

对于任何技术栈,都会有一个绕不过去的坎,那就是性能优化,而对于如何进行性能优化,最重要的前提就是需要知道具体的耗时分布,要知道耗时分布,就得打点(时间戳),一般的性能打点都是一些散点,比较凌乱,而本文要讲的 Tracing 则是性能打点的一种非常优雅的实现,它以瀑布流的形式呈现,非常直观,还有一个更直观的名字叫 火焰图

Tracing 顾名思义 —— 追踪每段耗时分布。

背景

image.png

上面这张图是 Flutter Engine 初始化过程中的一部分流程,非常直观的反应了执行流程中每个阶段的耗时分布。

Tracing 是 Chrome 开发者工具中强大的性能分析工具之一,它能记录 Chrome 所有进程间的各种活动。例如能记录每个进程中每个线程里 C++ 或者 JavaScript 方法的调用栈/耗时,不仅仅如此,还能看到视图 Layer 之间的层级关系,相关文档介绍 The Trace Event Profiling Tool (about:tracing)

本文会专注在 Flutter Engine 中 Tracing 原理与实践,会分为原理篇与实践篇,原理篇会涉及到具体实现,实践篇主要包括如何使用、分析、定制。

⚠️:Flutter 中用 Timeline 这个词代替了 Tracing,Flutter Devtool 也提供了 Timeline 工具(展示的就是 Tracing 结构的信息)。这两个词是一个对等的概念,下文提到的 Timeline 可以和 Tracing 对等。

原理篇

整个 Timeline 的过程主要包括初始化 Timeline 与记录 Tracing 信息两个部分。

▐ 初始化 Timeline

初始化 Timeline 包括四个过程:注册 Flag、设置 Flag、TimelineStream 初始化、Timeline 初始化。
注册 Flag

Flutter 中会注册非常多的 Flag 用于各种功能标记,对于 Timeline/Tracing 功能就是 timeline_streams 标实,具体如下:

/path/to/engine/src/third_party/dart/runtime/vm/timeline.cc

// 执行宏定义
DEFINE_FLAG(charp,
            timeline_streams,
            NULL,
            "Comma separated list of timeline streams to record. "
            "Valid values: all, API, Compiler, CompilerVerbose, Dart, "
            "Debugger, Embedder, GC, Isolate, and VM.");

// 展开后:
charp FLAG_timeline_streams = Flags::Register_charp(&FLAG_timeline_streams, 'timeline_streams', NULL, "Comma separated list of timeline streams to record. "
            "Valid values: all, API, Compiler, CompilerVerbose, Dart, "
            "Debugger, Embedder, GC, Isolate, and VM.");

其中 charp 为 typedef const char* charp;

真正执行的函数如下:

/path/to/engine/src/third_party/dart/runtime/vm/flags.cc

const char* Flags::Register_charp(charp* addr,
                                  const char* name,
                                  const char* default_value,
                                  const char* comment) {
  ASSERT(Lookup(name) == NULL);
  Flag* flag = new Flag(name, comment, addr, Flag::kString);
  AddFlag(flag);
  return default_value;
}

其中 addr_ 是一个 union 成员,初始值为当前注册函数的默认值为 NULL,即 FLAG_timeline_streams 初始值为 NULL。

注册 Flag 的过程就是定义了 FLAG_timeline_streams 标记。

设置 Flag

在 Flutter Engine 初始化的过程中,可以进行 DartVm 参数的透传,例如 —trace-startup,这个参数就可以记录启动时 Tracing 信息,会由如下方法进行设置:

path/to/engine/src/flutter/runtime/dart_vm.cc

char* flags_error = Dart_SetVMFlags(args.size(), args.data());

最终调用方法:

/path/to/engine/src/third_party/dart/runtime/vm/flags.cc

char* Flags::ProcessCommandLineFlags(int number_of_vm_flags,
                                     const char** vm_flags) {
...
while ((i < number_of_vm_flags) &&
         IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) {
    const char* option = vm_flags[i] + kPrefixLen;
    Parse(option);
    i++;
}
...
}

这里主要会进行 Flag 的有效性验证,关键步骤为 Parse 方法中的 SetFlagFromString

bool Flags::SetFlagFromString(Flag* flag, const char* argument) {
  ASSERT(!flag->IsUnrecognized());
  switch (flag->type_) {
    ...
    case Flag::kString: {
      *flag->charp_ptr_ = argument == NULL ? NULL : strdup(argument);
      break;
    }
    ....
  }
  flag->changed_ = true;
  return true;
}

会针对不同 Flag Type 设置不同变量,而这些变量是一个 union 结构体,如下:

union {
    void* addr_;
    bool* bool_ptr_;
    int* int_ptr_;
    uint64_t* uint64_ptr_;
    charp* charp_ptr_;
    FlagHandler flag_handler_;
    OptionHandler option_handler_;
}

根据 union 的特性,针对不同的 Flag Type,会得到不同值类型,可见之前定义的 FLAG_timeline_streams 值最终就会设置成透传的值。例如 —trace_startup 对应的值为 Compiler,Dart,Debugger,Embedder,GC,Isolate,VM。

设置 Flag 的过程就是具体设置了之前定义的 FLAG_timeline_streams 值。

TimelineStream 初始化

在 FLAG_timeline_streams 中非常多的类型值,每种都定义了不同的 Stream,初始化过程包括三个步骤:Declare Stream(申明)、Get Stream(获取)、Define Stream(定义)。

✎ Declare Stream

/path/to/engine/src/third_party/dart/runtime/vm/timeline.h

// stream 申明
#define TIMELINE_STREAM_DECLARE(name, fuchsia_name)                            \
  static TimelineStream stream_##name##_;
  TIMELINE_STREAM_LIST(TIMELINE_STREAM_DECLARE)
#undef TIMELINE_STREAM_DECLARE

// 展开后
static TimelineStream stream_API_;
static TimelineStream stream_Compiler_;
static TimelineStream stream_Dart_;
static TimelineStream stream_Embedder_;
....

Flutter Engine 中的 Timeline 信息为 stream_Embedder_,其它的 Timeline 也包括 Dart 层、API 层等等,本文主要会关注在 stream_Embedder_。

✎ Get Stream

/path/to/engine/src/third_party/dart/runtime/vm/timeline.h

// 获取 Stream
#define TIMELINE_STREAM_ACCESSOR(name, fuchsia_name)                           \
  static TimelineStream* Get##name##Stream() { return &stream_##name##_; }
  TIMELINE_STREAM_LIST(TIMELINE_STREAM_ACCESSOR)
#undef TIMELINE_STREAM_ACCESSOR

// 展开后
static TimelineStream* GetAPIStream() { return &stream_API_; }
static TimelineStream* GetDartStream() { return &stream_Dart_; }
static TimelineStream* GetEmbedderStream() { return &stream_Embedder_; }
...

设置了相应的静态获取方法。

Define Stream

/path/to/engine/src/third_party/dart/runtime/vm/timeline.cc

#define TIMELINE_STREAM_DEFINE(name, fuchsia_name)                             \
  TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false);
TIMELINE_STREAM_LIST(TIMELINE_STREAM_DEFINE)
#undef TIMELINE_STREAM_DEFINE

// 展开后
TimelineStream Timeline::stream_API_("API", "dart:api", false);
TimelineStream Timeline::stream_Dart_("Dart", "dart:dart", false);
TimelineStream Timeline::stream_Embedder_("Embedder", "dart:embedder", false);
...

Timeline 初始化

void Timeline::Init() {
  ASSERT(recorder_ == NULL);
  recorder_ = CreateTimelineRecorder();
  ASSERT(recorder_ != NULL);
  enabled_streams_ = GetEnabledByDefaultTimelineStreams();
// Global overrides.
#define TIMELINE_STREAM_FLAG_DEFAULT(name, fuchsia_name)                       \
  stream_##name##_.set_enabled(HasStream(enabled_streams_, #name));
  TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT)
#undef TIMELINE_STREAM_FLAG_DEFAULT
}

1、通过 CreateTimelineRecorder 创建 TimelineEventRecorder,如果需要获取启动 Tracing 信息会创建 TimelineEventEndlessRecorder,会记录无上限的 Trace 信息。
2、设置刚才创建的一系列 TimelineStream 实例的 set_enable 函数,后续在进行 Timeline 记录的时候都会查询是否 enable。

▐ 记录 Timeline 信息

上一部分主要讲了 Timeline 初始化准备的各种信息变量,这部分主要会讲记录 Tracing 信息的过程。

记录 Tracing 信息有非常多的调用方法,包括记录同步事件(TRACE_EVENT)、异步事件(TRACE_EVENT_ASYNC)、事件流(TRACE_FLOW_)。以下讲同步事件的调用过程,其他事件整个流程基本类似。

同步事件包括 TRACE_EVENT0 、TRACE_EVENT1、TRACE_EVENT2 等,以 TRACE_EVENT0 调用为例:

{
    TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots");
}

// 展开后
::fml::tracing::TraceEvent0("flutter", "Shell::CreateWithSnapshots");
::fml::tracing::ScopedInstantEnd __trace_end___LINE__("Shell::CreateWithSnapshots");

主要包括两个部分:

  • 记录阶段 TraceEvent0,记录当前信息
  • 标记结束 ScopedInstantEnd ,一般在作用域析构时调用

TraceEvent0

TraceEvent0 最终会调用如下方法:

path/to/engine/src/third_party/dart/runtime/vm/dart_api_impl.cc

DART_EXPORT void Dart_TimelineEvent(const char* label,
                                    int64_t timestamp0,
                                    int64_t timestamp1_or_async_id,
                                    Dart_Timeline_Event_Type type,
                                    intptr_t argument_count,
                                    const char** argument_names,
                                    const char** argument_values) {
...
TimelineStream* stream = Timeline::GetEmbedderStream();
  ASSERT(stream != NULL);
  TimelineEvent* event = stream->StartEvent();
...
switch (type) {
    case Dart_Timeline_Event_Begin:
      event->Begin(label, timestamp0);
      break;
    case Dart_Timeline_Event_End:
      event->End(label, timestamp0);
      break;
...
  }
...
event->Complete();
}

整个过程主要包括四个阶段:

  • TimelineStream::StartEvent:生成 TimelineEvent,其中Timeline::GetEmbedderStream() 即为初始化阶段的 stream_Embedder_。
  • TimelineEvent::Begin/End:记录起始、结束的时间等信息
  • TimelineEvent::Complete:完成当前记录
  • TimelineEventBlock::Finish:上报记录的信息

✎ TimelineStream::StartEvent

stream->StartEvent() 最终会调用如下方法产生 TimelineEvent:

/path/to/engine/src/third_party/dart/runtime/vm/timeline.cc

TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
  // Grab the current thread.
  OSThread* thread = OSThread::Current();
  ASSERT(thread != NULL);
  Mutex* thread_block_lock = thread->timeline_block_lock();
...
  thread_block_lock->Lock(); // 会一直持有,直到调用 CompleteEvent()
...
  TimelineEventBlock* thread_block = thread->timeline_block();

  if ((thread_block != NULL) && thread_block->IsFull()) {
    MutexLocker ml(&lock_);
    // Thread has a block and it is full:
    // 1) Mark it as finished.
    thread_block->Finish();
    // 2) Allocate a new block.
    thread_block = GetNewBlockLocked();
    thread->set_timeline_block(thread_block);
  } else if (thread_block == NULL) {
    MutexLocker ml(&lock_);
    // Thread has no block. Attempt to allocate one.
    thread_block = GetNewBlockLocked();
    thread->set_timeline_block(thread_block);
  }
  if (thread_block != NULL) {
    // NOTE: We are exiting this function with the thread's block lock held.
    ASSERT(!thread_block->IsFull());
    TimelineEvent* event = thread_block->StartEvent();
    return event;
  }
....
  thread_block_lock->Unlock();
  return NULL;
}

1、首先会调用线程锁,一直持有本次记录过程,直到调用 CompleteEvent()。
2、如果没有 TimelineEventBlock ,则首先会创建一个,并记录在当前线程中。
3、如果 TimelineEventBlock 满了,会先 Finish (见下文分析),再创建一个新的,并记录。
4、最后都会在 TimelineEventBlock 中创建一个新的 TimelineEvent,每个 TimelineEventBlock 创建的 TimelineEvent 会有数量限制,最多为 64 个。

⚠️:如果为 TimelineEventEndlessRecorder,则会无限创建 TimelineEventBlock,否则会有数量限制。

✎ TimelineEvent::Begin/End

/path/to/engine/src/third_party/dart/runtime/vm/timeline.cc

void TimelineEvent::Begin(const char* label,
                          int64_t micros,
                          int64_t thread_micros) {
  Init(kBegin, label);
  set_timestamp0(micros);
  set_thread_timestamp0(thread_micros);
}

这些阶段主要是记录具体的信息,包括:
1、Init: 记录事件标签名,事情类型(kBegin,kEnd),End 一般会在作用域析构时调用(下面会分析)。
2、micros: 记录系统启动后运行的时间戳。

3、thread_micros: 记录该线程CPU运行的时间戳。

✎ TimelineEvent::Complete

最终调用方法如下:

/path/to/engine/src/third_party/dart/runtime/vm/timeline.cc

void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
...
   // Grab the current thread.
  OSThread* thread = OSThread::Current();
  ASSERT(thread != NULL);
  // Unlock the thread's block lock.
  Mutex* thread_block_lock = thread->timeline_block_lock();
...
  thread_block_lock->Unlock();
}

一次记录结束后会调用 Complete 方法,并最终会释放一开始 Lock 的同步锁。

✎ TimelineEventBlock::Finish

在 TimelineStream::StartEvent 中创建的TimelineEventBlock 提到,默认最多是 64 个,满了之后会调用 Finsih 方法。

void TimelineEventBlock::Finish() {
...
  in_use_ = false;
#ifndef PRODUCT
  if (Service::timeline_stream.enabled()) {
    ServiceEvent service_event(NULL, ServiceEvent::kTimelineEvents);
    service_event.set_timeline_event_block(this);
    Service::HandleEvent(&service_event);
  }
#endif
}

最终会将事件信息发送给 ServiceIsolate 来处理,关于 ServiceIsolate 简单可以理解为后端服务,是由 Dart VM 初始化的时候创建的, DevTool 显示的信息(包括 Tracing 信息)都会和 ServiceIsolate 通信获取。

ScopedInstantEnd

class ScopedInstantEnd {
 public:
  ScopedInstantEnd(const char* str) : label_(str) {}
  ~ScopedInstantEnd() { TraceEventEnd(label_); }
 private:
  const char* label_;
  FML_DISALLOW_COPY_AND_ASSIGN(ScopedInstantEnd);
};

可以看到析构函数中会调用 TraceEventEnd 方法,也就是说离开了作用域就会调用 TraceEventEnd 方法,而 TraceEventEnd 方法最终调用的就是 TimelineEvent::End 阶段进行信息记录。

以上就是整体的 Tracing 信息的路由过程,实现上使用了大量的宏,宏在开发阶段还是方便实现,不过对于阅读源码来说会有一定的障碍,不能直观的进行代码搜索查找。

实践篇

主要介绍 Timeline 的使用、启动性能分析、有用的 Debug 参数介绍、以及添加自定义 Tracing 节点。

▐ Timeline 使用

Timeline 的使用在官方文档中已经有详细的说明,Using the Timeline view - Flutter 直接看文档即可。

▐ 启动性能分析

Timeline 工具仅仅只能分析 Flutter 页面启动之后的运行时情况,整个 Flutter 的启动过程完全是无法分析的,而启动/初始化过程也是比较关键的一环。

对于启动性能分析,官方文档描述甚少,目前只发现了这一处,Measuring app startup time - Flutter

启动性能分析包括三个步骤:添加启动性能参数、获取 Tracing 信息、分析。

添加启动参数

只有添加了特定的参数后才能获取启动时 Tracing 信息。

✎ Flutter App 场景

flutter run --trace-startup --profile

主要是通过 flutter cli 命令行参数运行 Flutter App,最终会在当前目录下生成 build/start_up_info.json 文件。

可惜的是这个文件只产出了四个关键的 Timestamp,远远达不到能够分析的地步,跟进 Flutter Tools 源码后,关键源码如下:

path/to/flutter/packages/flutter_tools/lib/src/tracing.dart

/// Download the startup trace information from the given observatory client and
/// store it to build/start_up_info.json.
Future<void> downloadStartupTrace(VMService observatory, { bool awaitFirstFrame = true }) async {
    final Tracing tracing = Tracing(observatory);

  final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
    awaitFirstFrame: awaitFirstFrame,
  );
......
 final Map<String, dynamic> traceInfo = <String, dynamic>{
    'engineEnterTimestampMicros': engineEnterTimestampMicros,
  };
......
    traceInfo['timeToFrameworkInitMicros'] = timeToFrameworkInitMicros;
......
    traceInfo['timeToFirstFrameRasterizedMicros'] = firstFrameRasterizedTimestampMicros - engineEnterTimestampMicros;
......
    traceInfo['timeToFirstFrameMicros'] = timeToFirstFrameMicros;
......
  traceInfo['timeAfterFrameworkInitMicros'] = firstFrameBuiltTimestampMicros - frameworkInitTimestampMicros;
......
traceInfoFile.writeAsStringSync(toPrettyJson(traceInfo));
}

可以看到关键的四个 Timestamp 被保存在 Map 进行输出到文件,最关键的一点是整个 timeline 数据其实都已经拿到了,于是可以进行如下改造:

/// Download the startup trace information from the given observatory client and
/// store it to build/start_up_info.json.
Future<void> downloadStartupTrace(VMService observatory, { bool awaitFirstFrame = true }) async {
    final Tracing tracing = Tracing(observatory);

  final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
    awaitFirstFrame: awaitFirstFrame,
  );
......
// 原来的 start_up_info.json 生成
    traceInfoFile.writeAsStringSync(toPrettyJson(traceInfo));
......
// 新增 start_up_trace_events.json 生成
    final String traceEventsFilePath = globals.fs.path.join(getBuildDirectory(), 'start_up_trace_events.json');
  final File traceEventsFile = globals.fs.file(traceEventsFilePath);
  final List<Map<String, dynamic>> events =
        List<Map<String, dynamic>>.from((timeline['traceEvents'] as List<dynamic>).cast<Map<String, dynamic>>());
  traceEventsFile.writeAsStringSync(toPrettyJson(events));
}

改造后会在当前目录下生成 build/start_up_trace_events.json 文件,并通过 chrome://tracing 打开查看。有一个注意点,在改动 flutter tools 代码后,需要重新生成 flutter command ,具体可以看文档。The flutter tool · flutter/flutter Wiki · GitHub

上面这个场景对于整个 Flutter App 来讲是完全可以进行启动性能分析了,但是对于 Add to App 的场景还是无法满足,因为这种场景无法通过 flutter cli 来进行参数透传。

**✎ Add To App 场景
**
对于这种场景,需要通过 Platform 层去透传参数。

Android

Android 侧参数透传方法如下:

path/to/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java

public FlutterEngine(
      @NonNull Context context,
      @NonNull FlutterLoader flutterLoader,
      @NonNull FlutterJNI flutterJNI,
      @NonNull PlatformViewsController platformViewsController,
      @Nullable String[] dartVmArgs,
      boolean automaticallyRegisterPlugins) {
......
}

通过实例化 FlutterEngine 时的构造参数 dartVmArgs 中添加 --trace-startup 即可。

new FlutterEngine(mPlatform.getApplication().getApplicationContext(),
                    FlutterLoader.getInstance(),new FlutterJNI(),new String[]{"--trace-startup"},true);

iOS

iOS 侧通过源码查看,对应的 FlutterEngine.mm 的构造参数中是没有对应的 dartVmArgs 参数透传。真正参数转换的地方如下:

path/to/engine/src/flutter/shell/platform/darwin/common/command_line.mm

fml::CommandLine CommandLineFromNSProcessInfo() {
  std::vector<std::string> args_vector;
  for (NSString* arg in [NSProcessInfo processInfo].arguments) {
    args_vector.emplace_back(arg.UTF8String);
  }
  return fml::CommandLineFromIterators(args_vector.begin(), args_vector.end());
}

通过 [NSProcessInfo processInfo].arguments 拿的命令行参数,无法通过自定义加入参数实现,对于从 XCode 启动 App 的可以通过编辑 schema 添加参数实现,示例如下:

image.png

但是绝大多数情况下,不会通过 XCode 来启动 App,因此还是需要通过修改 Engine 代码来实现参数传递。对此提了 PR 来支持 dartVm 参数的透传。

path/to/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm

- (instancetype)initWithDartVmArgs:(nullable NSArray<NSString*>*)args {
  return [self initWithPrecompiledDartBundle:nil dartVmArgs:args];
}

初始化 FlutterEngine.mm 中可以通过如下方式初始化:

_dartProject = [[FlutterDartProject alloc] initWithPrecompiledDartBundle:dartBundle dartVmArgs:@[@"--trace-startup"]];
_engine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:_dartProject allowHeadlessExecution:YES];

Android Systrace

对于 Android 设备来讲,还可以用 Android 独有的 Systrace 来看,不需要改任何 Flutter 相关的参数。

相关参考文档:
Understanding Systrace | Android Open Source Project
Overview of system tracing | Android Developers

获取 Tracing 文件

添加了启动参数之后,需要有工具进行查看,Flutter 默认提供的 DevTool 默认就能进行查看,按如下步骤:

拿到启动后的 Observatory 地址。
通过 flutter attach --debug-uri=observatory_url attach 到对应的服务,会生成一个 debugger/profiler 地址。
打开 debugger/profiler 地址后就是 Fluuter 默认的 DevTool 工具,点击 timeline 按钮即可打开 Tracing 内容。

分析 Tracing 文件

关于 Tracing 工具的使用可以查看相关 Chrome 文档, The Trace Event Profiling Tool (about:tracing)

展示的信息比较直观,对于启动性能分析,能非常直观的看到各个部分的耗时情况,下图是 Flutter 启动时 iOS 上的各个耗时阶段的大致分布,图的左边,可以看到各个阶段执行对应的线程。

image.png

▐ Debug 参数

上面介绍了如何获取 Tracing 的方法,生成的 Tracing 耗时分布主要包括各个阶段的耗时,但是还并不是包含所有的阶段,介绍两个有用的 Debug 参数,其他相关参数参考文档 [Debug flags: performance - Flutter
](链接地址https://flutter.dev/docs/testing/code-debugging?spm=ata.13261165.0.0.32ca24d41mrBFF#debug-flags-performance)
debugProfilePaintsEnabled

path/to/flutter/packages/flutter/lib/src/rendering/debug.dart

bool debugProfilePaintsEnabled = false;

这个参数会在渲染 Paint 阶段,显示所有 Paint 时节点的遍历情况,可以根据这些信息查看是否有无用的节点 Paint

debugProfileBuildsEnabled

path/to/flutter/packages/flutter/lib/src/widgets/debug.dart

bool debugProfileBuildsEnabled = false;

这个参数会在 Widget Build 阶段,显示所有 Widget 节点 Build 时的遍历情况,可以根据这些信息查看是否有无用的节点 Build。

image.png

上图把 build、paint 阶段的过程全都显示出来了,有了这些信息后,还需要结合自身的业务逻辑分析 Widget Build/Paint 是否合理,是否执行了无用的操作,然后进行优化。

自定义 Tracing 节点

对于默认没有打点的地方,如果自己需要查看其耗时,则可以自行进行打点。例如需要查看创建 IOSContext 的耗时,则可以进行如下打点:

std::unique_ptr<IOSContext> IOSContext::Create(IOSRenderingAPI rendering_api) {
  TRACE_EVENT0("flutter", "IOSContext::Create");
  ......
  FML_CHECK(false);
  return nullptr;
}

最终会反应在 Tracing 上,如下图:

image.png

后记

本文主要分析了 Tracing 在 Flutter 上的实现以及一些实践,Tracing 是 Chrome 实现的一种标准格式,任何技术栈的性能分析都可以生成这种标准格式,然后利用现成的 Chrome DevTool 工具打开即可分析,非常直观,能启到事半功倍的效果。

关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

image.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:

淘系技术部隶旗下包含淘宝技术、天猫技术、农村淘宝技术、闲鱼、iHome等团队和业务,是一支是具有商业和技术双重基因的螺旋体。 我们致力于成为全球最懂商业的技术创新团队,打造消费者和商家一体化的新零售智能商业平台,创新商业赛道。随着新零售业务的持续探索与快速发展,我们不断吸引用户增长、机器学习、视觉算法、音视频通信、数字媒体、端侧智能等领域全球顶尖专业人才加入,让科技引领面向未来的商业创新和进步。欢迎投递简历至ruoqi.zlj@taobao.com

官方博客
淘系开源,欢迎star哟