diff --git a/ReactCommon/fabric/mounting/Differentiator.cpp b/ReactCommon/fabric/mounting/Differentiator.cpp index 2c7e997b3d9..84d4931c28a 100644 --- a/ReactCommon/fabric/mounting/Differentiator.cpp +++ b/ReactCommon/fabric/mounting/Differentiator.cpp @@ -6,6 +6,7 @@ #include "Differentiator.h" #include +#include #include #include #include "ShadowView.h" @@ -13,6 +14,69 @@ namespace facebook { namespace react { +/* + * Extremely simple and naive implementation of a map. + * The map is simple but it's optimized for particular constraints that we have + * here. + * + * A regular map implementation (e.g. `std::unordered_map`) has some basic + * performance guarantees like constant average insertion and lookup complexity. + * This is nice, but it's *average* complexity measured on a non-trivial amount + * of data. The regular map is a very complex data structure that using hashing, + * buckets, multiple comprising operations, multiple allocations and so on. + * + * In our particular case, we need a map for `int` to `void *` with a dozen + * values. In these conditions, nothing can beat a naive implementation using a + * stack-allocated vector. And this implementation is exactly this: no + * allocation, no hashing, no complex branching, no buckets, no iterators, no + * rehashing, no other guarantees. It's crazy limited, unsafe, and performant on + * a trivial amount of data. + * + * Besides that, we also need to optimize for insertion performance (the case + * where a bunch of views appears on the screen first time); in this + * implementation, this is as performant as vector `push_back`. + */ +template +class TinyMap final { + public: + using Pair = std::pair; + using Iterator = Pair *; + + inline Iterator begin() { + return (Pair *)vector_; + } + + inline Iterator end() { + return nullptr; + } + + inline Iterator find(KeyT key) { + for (auto &item : vector_) { + if (item.first == key) { + return &item; + } + } + + return end(); + } + + inline void insert(Pair pair) { + assert(pair.first != 0); + vector_.push_back(pair); + } + + inline void erase(Iterator iterator) { + static_assert( + std::is_same::value, + "The collection is designed to store only `Tag`s as keys."); + // Zero is a invalid tag. + iterator->first = 0; + } + + private: + better::small_vector vector_; +}; + static void sliceChildShadowNodeViewPairsRecursively( ShadowViewNodePair::List &pairList, Point layoutOffset, @@ -70,7 +134,7 @@ static void calculateShadowViewMutations( auto index = int{0}; // Maps inserted node tags to pointers to them in `newChildPairs`. - auto insertedPairs = better::map{}; + auto insertedPairs = TinyMap{}; // Lists of mutations auto createMutations = ShadowViewMutation::List{};