Fabric: Introducing Better: For faster, clear and ideomatic codebase

Summary:
`Better` is a trivial collection of basic tools borrowed from other low-level general purpose libraries (like Folly, Abseil or Boost). The main goals of Better:
 - Make the codebase more portable;
 - Make the dependency list explicit (by decoupling it as a dependency list of Better);
 - Make relying on modern C++ patterns and tools in code simple and easy.
 - Make executing experiments with different dependencies easier.

 As a first example usage, this diff replaces std::unordered_map with an efficient one from folly on the one of the hottest paths.

Reviewed By: JoshuaGross

Differential Revision: D13944565

fbshipit-source-id: 5fa2c4abe6c17f7361eddcc25f968b6440d5d9db
This commit is contained in:
Valentin Shergin 2019-02-08 12:28:56 -08:00 committed by Facebook Github Bot
parent 7ecf55fc9d
commit fd3b8f2000
10 changed files with 341 additions and 8 deletions

55
ReactCommon/better/BUCK Normal file
View File

@ -0,0 +1,55 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "better",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "better",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(),
force_static = True,
macosx_tests_override = [],
platforms = (ANDROID, APPLE),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [],
visibility = ["PUBLIC"],
deps = [
"xplat//fbsystrace:fbsystrace",
"xplat//folly:headers_only",
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//third-party/glog:glog",
],
)

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
namespace better {
/*
* `Better` is a trivial collection of basic tools borrowed from other low-level
* general purpose libraries (like Folly, Abseil or Boost). The main goals of
* Better:
* - Make the codebase more portable;
* - Make the dependency list explicit (by decoupling it as a dependency list of
* Better);
* - Make relying on modern C++ patterns and tools in code simple and easy.
* - Make executing experiments with different dependencies easier.
*
* What should be part of Better and what should not? Should I add some piece of
* functionality in the Better? Here is a quick checklist.
*
* If one of the following is true, yes, go for it:
* - If some feature is already in some future C++ standard (possibly in draft
* stage) and it's already implemented in some 3rd party library.
* - If some standardized feature of C++ is implemented in the standard not in
* the most efficient way (because the standard enforces some tricky constraints
* (like always-valid iterators) which nobody uses and should use), but you have
* a library that does it right providing exact same interface.
*
* If one of the following is true, please do *NOT* do it (at least as part of
* the library):
* - You want to use some very fancy pattern that your favorite library (but
* nothing else) provides, and You want to make this pattern very command in the
* code base. Your hope is that this pattern will conquer the world and be
* a part of the C++ standard eventually.
* - You favorite library provides some general purpose container that 10x times
* faster than the standard one, so You want to use that in the code base. That
* container does not have compatible API though (because it's a clear trade-off
* with efficiency, of course).
*/
/*
* Configuration
*/
/*
* Enables using Folly containers instead of standard ones (such as map, vector,
* string, optional and etc.)
*/
#define BETTER_USE_FOLLY_CONTAINERS
} // namespace better
} // namespace facebook

45
ReactCommon/better/map.h Normal file
View File

@ -0,0 +1,45 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/container/F14Map.h>
#else
#include <unordered_map>
#endif
namespace facebook {
namespace better {
/*
* Note: In Better, `map` aliases to `unorderd_map` because everyone agrees that
* an *ordered* map is nonsense and was a huge mistake for standardization. If
* you need an *ordered* map, feel free to introduce that as
* `better::ordered_map`.
*/
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename... Ts>
using map = folly::F14FastMap<Ts...>;
#else
template <typename... Ts>
using map = std::unordered_map<Ts...>;
#endif
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <folly/SharedMutex.h>
#include <mutex>
#include <shared_mutex>
namespace facebook {
namespace better {
template <typename T>
using shared_lock = std::shared_lock<T>;
template <typename T>
using shared_mutex = folly::SharedMutex<T>;
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/Optional.h>
#else
#include <optional>
#endif
namespace facebook {
namespace better {
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename Value>
using optional = folly::Optional<Value>;
#else
template <typename Value>
using optional = std::optional<Value>;
#endif
} // namespace better
} // namespace facebook

38
ReactCommon/better/set.h Normal file
View File

@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/container/F14Set.h>
#else
#include <unordered_set>
#endif
namespace facebook {
namespace better {
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename... Ts>
using set = folly::F14FastSet<Ts...>;
#else
template <typename... Ts>
using set = std::unordered_set<Ts...>;
#endif
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/fbstring.h>
#else
#include <string>
#endif
namespace facebook {
namespace better {
#ifdef BETTER_USE_FOLLY_CONTAINERS
using string = folly::fbstring;
#else
using string = std::string;
#endif
} // namespace better
} // namespace facebook

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <better/better.h>
#ifdef BETTER_USE_FOLLY_CONTAINERS
#include <folly/fbvector.h>
#else
#include <vector>
#endif
namespace facebook {
namespace better {
#ifdef BETTER_USE_FOLLY_CONTAINERS
template <typename... Ts>
using vector = folly::fbvector<Ts...>;
#else
template <typename... Ts>
using vector = std::vector<Ts...>;
#endif
} // namespace better
} // namespace facebook

View File

@ -56,6 +56,7 @@ rn_xplat_cxx_library(
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//third-party/glog:glog",
react_native_xplat_target("better:better"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/events:events"),

View File

@ -5,6 +5,7 @@
#include "Differentiator.h"
#include <better/map.h>
#include <react/core/LayoutableShadowNode.h>
#include "ShadowView.h"
@ -65,7 +66,7 @@ static void calculateShadowViewMutations(
return;
}
std::unordered_map<Tag, ShadowViewNodePair> insertedPaires;
better::map<Tag, ShadowViewNodePair> insertedPairs;
int index = 0;
ShadowViewMutationList createMutations = {};
@ -116,7 +117,7 @@ static void calculateShadowViewMutations(
insertMutations.push_back(ShadowViewMutation::InsertMutation(
parentShadowView, newChildPair.shadowView, index));
insertedPaires.insert({newChildPair.shadowView.tag, newChildPair});
insertedPairs.insert({newChildPair.shadowView.tag, newChildPair});
}
// Stage 3: Collecting `Delete` and `Remove` mutations
@ -129,9 +130,9 @@ static void calculateShadowViewMutations(
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
parentShadowView, oldChildPair.shadowView, index));
const auto &it = insertedPaires.find(oldChildPair.shadowView.tag);
const auto &it = insertedPairs.find(oldChildPair.shadowView.tag);
if (it == insertedPaires.end()) {
if (it == insertedPairs.end()) {
// The old view was *not* (re)inserted.
// We have to generate `delete` mutation and apply the algorithm
// recursively.
@ -163,11 +164,11 @@ static void calculateShadowViewMutations(
newGrandChildPairs);
}
// In any case we have to remove the view from `insertedPaires` as
// In any case we have to remove the view from `insertedPairs` as
// indication that the view was actually removed (which means that
// the view existed before), hence we don't have to generate
// `create` mutation.
insertedPaires.erase(it);
insertedPairs.erase(it);
}
}
@ -176,8 +177,8 @@ static void calculateShadowViewMutations(
index++) {
const auto &newChildPair = newChildPairs[index];
if (insertedPaires.find(newChildPair.shadowView.tag) ==
insertedPaires.end()) {
if (insertedPairs.find(newChildPair.shadowView.tag) ==
insertedPairs.end()) {
// The new view was (re)inserted, so there is no need to create it.
continue;
}