Open8

【Flutter】Widget の == operator を override することが推奨されていない理由を深ぼりたい

づだづだ

元ドキュメントはこれ

Avoid overriding operator == on Widget objects. While it might seem like it would help by avoiding unnecessary rebuilds, in practice it hurts performance because it results in O(N²) behavior. The only exception to this rule is leaf widgets (widgets with no children), in the specific case where comparing the properties of the widget is likely to be significantly more efficient than rebuilding the widget and where the widget will rarely change configuration. Even in such cases, it is generally preferable to rely on caching the widgets, because even one override of operator == can result in across-the-board performance degradation as the compiler can no longer assume that the call is always static.

https://docs.flutter.dev/perf/best-practices#pitfalls

== operator を上書きすることで、Element.updateChild の初めの分岐、child.widget == newWidget を true にして、リビルドを伝播させないようにすると、そこには落とし穴(pitfalls)があるよって内容。

深掘りたい理由

  • == operator の上書きが、なぜ O(N²) になるのかがわからない。
    • そもそも、どう上書きする前提なのかが明言されてない。いくつかのプロパティの比較に置き換えるってことなのかな?
づだづだ

== operator の中身をプロパティの数だけやると、計算量が多くなっちゃうって話なのかな?
でも O(N^2) ではない気がする…

づだづだ

2020 年に、質問の Issue が上がってる(よく見たら Remi さんだった)
https://github.com/flutter/flutter/issues/49490

identical での比較の話になってるけど、Element 内で identical 比較してる実装は見つからない…

Widget のデフォルトの == operator も identical じゃない。
わからん…

  
  
  bool operator ==(Object other) => super == other;
づだづだ

わかったかも
デフォルトの == operator は、identical、つまり参照の比較になっていて、高速な計算が実現できる。
== operator を上書きして、プロパティの比較にしてしまうと、プロパティが増えるたびに、== operator 計算量の最大値が増えていってしまう。

づだづだ

It was likely about non-leaf widgets.

If a widget has a child, and that child too overrides ==, then we could end up effectively comparing the entire widget tree.

But I don't see any issue with leaf widgets though.

https://github.com/flutter/flutter/issues/49490#issuecomment-578871743

ここが分からんのよなぁ…
child の == が override されていた場合、なんで全ての Widget を比較することになるの??

づだづだ

There are two things that can affect performance: the performance of the comparison itself, and the performance of the framework due to changes in comparison semantics. The former is probably relatively small. The latter can be much bigger depending on the app.
https://github.com/flutter/flutter/issues/49490#issuecomment-584415511

いや、そうなんよ
比較の計算量より、child.update(newWidget) の方がデカくねって話。

づだづだ

== operator の運用は難しいから、下手なことはしないほうがいいってまとまってる感じする
ただ、ListView のアイテムの == operator は面白いし、有効っぽいな
25ms から 16ms へのインパクトって書いてるし、60fps の最小フレームに影響してるから、採用の価値はかなり高い。

https://github.com/flutter/flutter/issues/49490#issuecomment-583819096