Flutter的语义搜索中包含非线性代码,这可能导致运行速度非常慢,

bqjvbblv  于 6个月前  发布在  Flutter
关注(0)|答案(3)|浏览(52)

当在linux上运行一个大型应用(DevTools)时,我注意到该应用会冻结。当我通过DevTools对其进行性能分析时,我发现它在语义代码上花费了很多时间。
_drawFrame -> _handleDrawFrame -> ... -> RendererBinding.drawFrame -> ... -> PipelineOwner.flushSemantics -> ... -> SemanticsNode.attach.
观察这段代码,很明显为什么它会花费这么多时间:

@visibleForTesting
  void attach(SemanticsOwner owner) {
   ...
    while (owner._nodes.containsKey(id)) {
      // Ids may repeat if the Flutter has generated > 2^16 ids. We need to keep
      // regenerating the id until we found an id that is not used.
      _id = _generateNewId();
    }
    owner._nodes[id] = this;
    ...
  }

  static const int _maxFrameworkAccessibilityIdentifier = (1<<16) - 1;
  static int _lastIdentifier = 0;
  static int _generateNewId() {
    _lastIdentifier = (_lastIdentifier + 1) % _maxFrameworkAccessibilityIdentifier;
    return _lastIdentifier;
  }

  final Map<int, SemanticsNode> _nodes = <int, SemanticsNode>{};

需要注意的是:如果这仅仅是@visibleForTesting,那么我只会假设它只应该在flutter test中运行,而不是与flutter run / flutter build一起运行。
我们可以看到这里只有64k个可用的id。如果系统中有很多节点,那么循环很可能会在找到一个空闲id之前持续很长时间。如果我们这样做很多次,我们就会得到二次方行为 - 这是非常糟糕的。
我认为这应该使用一种可以在O(1)或O(log-n)内找到空闲id的不同数据结构。

tcbh2hod

tcbh2hod1#

@visibleForTesting 的意思并不是你所想的。它意味着这通常是一个私有方法,但我们将其暴露出来,以便在某些单元测试中使用。但是这个方法对于实际运行无障碍系统至关重要。

是的,整个方案似乎可疑。一旦到达 2^16 ,它将进行一次爬取。需要检查的是,是否有可能重新分配分割?为什么大多数ID被保留给引擎?真的是双方都创建了相同数量的无障碍节点(我怀疑)吗?

hrysbysz

hrysbysz2#

实际上,将ID范围分成两部分的做法是错误的。应该在2^31上进行分割,而不是在2^16上进行分割,以将其分成两半...将会打开一些PRs

相关问题