mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Fabric: TinyMap in Differentiator
Summary: Diffing algorithm uses a small map for every layer of shadow tree; that's a lot of maps. Luckily, it does not need a complex feature-full map (which is not free to allocate and use), it needs a tiny map with a dozen values. Why do we need to pay for what we don't use? This diff introduces a trivial map optimized for constraints that we have here. (See more details in the code.) Reviewed By: mdvacca Differential Revision: D15200495 fbshipit-source-id: d859b68b9543253840b403e7430f945a0b76d87b
This commit is contained in:
committed by
Facebook Github Bot
parent
c5f0969fba
commit
01523ae660
@@ -6,6 +6,7 @@
|
||||
#include "Differentiator.h"
|
||||
|
||||
#include <better/map.h>
|
||||
#include <better/small_vector.h>
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
#include <react/debug/SystraceSection.h>
|
||||
#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 <typename KeyT, typename ValueT, int DefaultSize = 16>
|
||||
class TinyMap final {
|
||||
public:
|
||||
using Pair = std::pair<KeyT, ValueT>;
|
||||
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<KeyT, Tag>::value,
|
||||
"The collection is designed to store only `Tag`s as keys.");
|
||||
// Zero is a invalid tag.
|
||||
iterator->first = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
better::small_vector<Pair, DefaultSize> 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<Tag, ShadowViewNodePair const *>{};
|
||||
auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
|
||||
|
||||
// Lists of mutations
|
||||
auto createMutations = ShadowViewMutation::List{};
|
||||
|
||||
Reference in New Issue
Block a user