Replace CompactValue with StyleValueHandle and StyleValuePool (#1534)
Summary: X-link: https://github.com/facebook/react-native/pull/42131 Pull Request resolved: https://github.com/facebook/yoga/pull/1534 Now that the storage method is a hidden implementation detail, this changes the underlying data structure used to store styles, from `CompactValue` (a customized 32-bit float with tag bits), to `StyleValuePool`. This new structure operates on 16-bit handles, and a shared small buffer. The vast majority of real-world values can be stored directly in the handle, but we allow arbitrary 32 bit (and soon 64-bit) values to be stored, where the handle then becomes an index into the styles buffer. This results in a real-world memory usage win, while also letting us store the 64-bit values we are wanting to use for math function support (compared to doubling the storage requirements). This does seem to make style reads slower, which due to their heavy frequency, does have a performance impact observable by synthetics. In an example laying out a tree of 10,000 nodes, we originally read from `StyleValuePool` 2.4 million times. This originally resulted in a ~10% regression, but when combined with the changes in the last diff, most style reads become simple bitwise operations on the handle, and we are actually 14% faster than before. | | Before | After | Δ | | `sizeof(yoga::Style)` | 208B | 144B | -64B/-31% | | `sizeof(yoga::Node)` | 640B | 576B | -64B/-10% | | `sizeof(YogaLayoutableShadowNode) ` | 920B | 856B | -64B/-7% | | `sizeof(YogaLayoutableShadowNode) + sizeof(YogaStylableProps)` | 1296B | 1168B | -128B/-10% | | `sizeof(ViewShadowNode)` | 920B | 856B | -64B/-7% | | `sizeof(ViewShadowNode) + sizeof(ViewShadowNodeProps)` | 2000B | 1872B | -128B/-6% | | "Huge nested layout" microbenchmark (M1 Ultra) | 11.5ms | 9.9ms | -1.6ms/-14% | | Quest Store C++ heap usage (avg over 10 runs) | 86.2MB | 84.9MB | -1.3MB/-1.5% | Reviewed By: joevilches Differential Revision: D52223122 fbshipit-source-id: 990f4b7e991e8e22d198ce20f7da66d9c6ba637b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
395c596695
commit
67154d47a3
@@ -7,7 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
@@ -29,8 +28,8 @@
|
||||
#include <yoga/enums/Unit.h>
|
||||
#include <yoga/enums/Wrap.h>
|
||||
#include <yoga/numeric/FloatOptional.h>
|
||||
#include <yoga/style/CompactValue.h>
|
||||
#include <yoga/style/StyleLength.h>
|
||||
#include <yoga/style/StyleValuePool.h>
|
||||
|
||||
namespace facebook::yoga {
|
||||
|
||||
@@ -113,94 +112,94 @@ class YG_EXPORT Style {
|
||||
}
|
||||
|
||||
FloatOptional flex() const {
|
||||
return flex_;
|
||||
return pool_.getNumber(flex_);
|
||||
}
|
||||
void setFlex(FloatOptional value) {
|
||||
flex_ = value;
|
||||
pool_.store(flex_, value);
|
||||
}
|
||||
|
||||
FloatOptional flexGrow() const {
|
||||
return flexGrow_;
|
||||
return pool_.getNumber(flexGrow_);
|
||||
}
|
||||
void setFlexGrow(FloatOptional value) {
|
||||
flexGrow_ = value;
|
||||
pool_.store(flexGrow_, value);
|
||||
}
|
||||
|
||||
FloatOptional flexShrink() const {
|
||||
return flexShrink_;
|
||||
return pool_.getNumber(flexShrink_);
|
||||
}
|
||||
void setFlexShrink(FloatOptional value) {
|
||||
flexShrink_ = value;
|
||||
pool_.store(flexShrink_, value);
|
||||
}
|
||||
|
||||
Style::Length flexBasis() const {
|
||||
return (Style::Length)flexBasis_;
|
||||
return pool_.getLength(flexBasis_);
|
||||
}
|
||||
void setFlexBasis(Style::Length value) {
|
||||
flexBasis_ = CompactValue(value);
|
||||
pool_.store(flexBasis_, value);
|
||||
}
|
||||
|
||||
Style::Length margin(Edge edge) const {
|
||||
return (Style::Length)margin_[yoga::to_underlying(edge)];
|
||||
return pool_.getLength(margin_[yoga::to_underlying(edge)]);
|
||||
}
|
||||
void setMargin(Edge edge, Style::Length value) {
|
||||
margin_[yoga::to_underlying(edge)] = CompactValue(value);
|
||||
pool_.store(margin_[yoga::to_underlying(edge)], value);
|
||||
}
|
||||
|
||||
Style::Length position(Edge edge) const {
|
||||
return (Style::Length)position_[yoga::to_underlying(edge)];
|
||||
return pool_.getLength(position_[yoga::to_underlying(edge)]);
|
||||
}
|
||||
void setPosition(Edge edge, Style::Length value) {
|
||||
position_[yoga::to_underlying(edge)] = CompactValue(value);
|
||||
pool_.store(position_[yoga::to_underlying(edge)], value);
|
||||
}
|
||||
|
||||
Style::Length padding(Edge edge) const {
|
||||
return (Style::Length)padding_[yoga::to_underlying(edge)];
|
||||
return pool_.getLength(padding_[yoga::to_underlying(edge)]);
|
||||
}
|
||||
void setPadding(Edge edge, Style::Length value) {
|
||||
padding_[yoga::to_underlying(edge)] = CompactValue(value);
|
||||
pool_.store(padding_[yoga::to_underlying(edge)], value);
|
||||
}
|
||||
|
||||
Style::Length border(Edge edge) const {
|
||||
return (Style::Length)border_[yoga::to_underlying(edge)];
|
||||
return pool_.getLength(border_[yoga::to_underlying(edge)]);
|
||||
}
|
||||
void setBorder(Edge edge, Style::Length value) {
|
||||
border_[yoga::to_underlying(edge)] = CompactValue(value);
|
||||
pool_.store(border_[yoga::to_underlying(edge)], value);
|
||||
}
|
||||
|
||||
Style::Length gap(Gutter gutter) const {
|
||||
return (Style::Length)gap_[yoga::to_underlying(gutter)];
|
||||
return pool_.getLength(gap_[yoga::to_underlying(gutter)]);
|
||||
}
|
||||
void setGap(Gutter gutter, Style::Length value) {
|
||||
gap_[yoga::to_underlying(gutter)] = CompactValue(value);
|
||||
pool_.store(gap_[yoga::to_underlying(gutter)], value);
|
||||
}
|
||||
|
||||
Style::Length dimension(Dimension axis) const {
|
||||
return (Style::Length)dimensions_[yoga::to_underlying(axis)];
|
||||
return pool_.getLength(dimensions_[yoga::to_underlying(axis)]);
|
||||
}
|
||||
void setDimension(Dimension axis, Style::Length value) {
|
||||
dimensions_[yoga::to_underlying(axis)] = CompactValue(value);
|
||||
pool_.store(dimensions_[yoga::to_underlying(axis)], value);
|
||||
}
|
||||
|
||||
Style::Length minDimension(Dimension axis) const {
|
||||
return (Style::Length)minDimensions_[yoga::to_underlying(axis)];
|
||||
return pool_.getLength(minDimensions_[yoga::to_underlying(axis)]);
|
||||
}
|
||||
void setMinDimension(Dimension axis, Style::Length value) {
|
||||
minDimensions_[yoga::to_underlying(axis)] = CompactValue(value);
|
||||
pool_.store(minDimensions_[yoga::to_underlying(axis)], value);
|
||||
}
|
||||
|
||||
Style::Length maxDimension(Dimension axis) const {
|
||||
return (Style::Length)maxDimensions_[yoga::to_underlying(axis)];
|
||||
return pool_.getLength(maxDimensions_[yoga::to_underlying(axis)]);
|
||||
}
|
||||
void setMaxDimension(Dimension axis, Style::Length value) {
|
||||
maxDimensions_[yoga::to_underlying(axis)] = CompactValue(value);
|
||||
pool_.store(maxDimensions_[yoga::to_underlying(axis)], value);
|
||||
}
|
||||
|
||||
FloatOptional aspectRatio() const {
|
||||
return aspectRatio_;
|
||||
return pool_.getNumber(aspectRatio_);
|
||||
}
|
||||
void setAspectRatio(FloatOptional value) {
|
||||
aspectRatio_ = value;
|
||||
pool_.store(aspectRatio_, value);
|
||||
}
|
||||
|
||||
bool horizontalInsetsDefined() const {
|
||||
@@ -448,19 +447,21 @@ class YG_EXPORT Style {
|
||||
alignItems_ == other.alignItems_ && alignSelf_ == other.alignSelf_ &&
|
||||
positionType_ == other.positionType_ && flexWrap_ == other.flexWrap_ &&
|
||||
overflow_ == other.overflow_ && display_ == other.display_ &&
|
||||
inexactEquals(flex_, other.flex_) &&
|
||||
inexactEquals(flexGrow_, other.flexGrow_) &&
|
||||
inexactEquals(flexShrink_, other.flexShrink_) &&
|
||||
inexactEquals(flexBasis_, other.flexBasis_) &&
|
||||
inexactEquals(margin_, other.margin_) &&
|
||||
inexactEquals(position_, other.position_) &&
|
||||
inexactEquals(padding_, other.padding_) &&
|
||||
inexactEquals(border_, other.border_) &&
|
||||
inexactEquals(gap_, other.gap_) &&
|
||||
inexactEquals(dimensions_, other.dimensions_) &&
|
||||
inexactEquals(minDimensions_, other.minDimensions_) &&
|
||||
inexactEquals(maxDimensions_, other.maxDimensions_) &&
|
||||
inexactEquals(aspectRatio_, other.aspectRatio_);
|
||||
numbersEqual(flex_, pool_, other.flex_, other.pool_) &&
|
||||
numbersEqual(flexGrow_, pool_, other.flexGrow_, other.pool_) &&
|
||||
numbersEqual(flexShrink_, pool_, other.flexShrink_, other.pool_) &&
|
||||
lengthsEqual(flexBasis_, pool_, other.flexBasis_, other.pool_) &&
|
||||
lengthsEqual(margin_, pool_, other.margin_, other.pool_) &&
|
||||
lengthsEqual(position_, pool_, other.position_, other.pool_) &&
|
||||
lengthsEqual(padding_, pool_, other.padding_, other.pool_) &&
|
||||
lengthsEqual(border_, pool_, other.border_, other.pool_) &&
|
||||
lengthsEqual(gap_, pool_, other.gap_, other.pool_) &&
|
||||
lengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) &&
|
||||
lengthsEqual(
|
||||
minDimensions_, pool_, other.minDimensions_, other.pool_) &&
|
||||
lengthsEqual(
|
||||
maxDimensions_, pool_, other.maxDimensions_, other.pool_) &&
|
||||
numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_);
|
||||
}
|
||||
|
||||
bool operator!=(const Style& other) const {
|
||||
@@ -468,23 +469,57 @@ class YG_EXPORT Style {
|
||||
}
|
||||
|
||||
private:
|
||||
using Dimensions = std::array<CompactValue, ordinalCount<Dimension>()>;
|
||||
using Edges = std::array<CompactValue, ordinalCount<Edge>()>;
|
||||
using Gutters = std::array<CompactValue, ordinalCount<Gutter>()>;
|
||||
using Dimensions = std::array<StyleValueHandle, ordinalCount<Dimension>()>;
|
||||
using Edges = std::array<StyleValueHandle, ordinalCount<Edge>()>;
|
||||
using Gutters = std::array<StyleValueHandle, ordinalCount<Gutter>()>;
|
||||
|
||||
static inline bool numbersEqual(
|
||||
const StyleValueHandle& lhsHandle,
|
||||
const StyleValuePool& lhsPool,
|
||||
const StyleValueHandle& rhsHandle,
|
||||
const StyleValuePool& rhsPool) {
|
||||
return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) ||
|
||||
(lhsPool.getNumber(lhsHandle) == rhsPool.getNumber(rhsHandle));
|
||||
}
|
||||
|
||||
static inline bool lengthsEqual(
|
||||
const StyleValueHandle& lhsHandle,
|
||||
const StyleValuePool& lhsPool,
|
||||
const StyleValueHandle& rhsHandle,
|
||||
const StyleValuePool& rhsPool) {
|
||||
return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) ||
|
||||
(lhsPool.getLength(lhsHandle) == rhsPool.getLength(rhsHandle));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
static inline bool lengthsEqual(
|
||||
const std::array<StyleValueHandle, N>& lhs,
|
||||
const StyleValuePool& lhsPool,
|
||||
const std::array<StyleValueHandle, N>& rhs,
|
||||
const StyleValuePool& rhsPool) {
|
||||
return std::equal(
|
||||
lhs.begin(),
|
||||
lhs.end(),
|
||||
rhs.begin(),
|
||||
rhs.end(),
|
||||
[&](const auto& lhs, const auto& rhs) {
|
||||
return lengthsEqual(lhs, lhsPool, rhs, rhsPool);
|
||||
});
|
||||
}
|
||||
|
||||
Style::Length computeColumnGap() const {
|
||||
if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) {
|
||||
return (Style::Length)gap_[yoga::to_underlying(Gutter::Column)];
|
||||
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Column)]);
|
||||
} else {
|
||||
return (Style::Length)gap_[yoga::to_underlying(Gutter::All)];
|
||||
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
Style::Length computeRowGap() const {
|
||||
if (gap_[yoga::to_underlying(Gutter::Row)].isDefined()) {
|
||||
return (Style::Length)gap_[yoga::to_underlying(Gutter::Row)];
|
||||
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Row)]);
|
||||
} else {
|
||||
return (Style::Length)gap_[yoga::to_underlying(Gutter::All)];
|
||||
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,27 +527,27 @@ class YG_EXPORT Style {
|
||||
const {
|
||||
if (layoutDirection == Direction::LTR &&
|
||||
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Start)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
|
||||
} else if (
|
||||
layoutDirection == Direction::RTL &&
|
||||
edges[yoga::to_underlying(Edge::End)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::End)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Left)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Left)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Left)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Horizontal)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
|
||||
} else {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::All)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
Style::Length computeTopEdge(const Edges& edges) const {
|
||||
if (edges[yoga::to_underlying(Edge::Top)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Top)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Top)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Vertical)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
|
||||
} else {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::All)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,27 +555,27 @@ class YG_EXPORT Style {
|
||||
const {
|
||||
if (layoutDirection == Direction::LTR &&
|
||||
edges[yoga::to_underlying(Edge::End)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::End)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
|
||||
} else if (
|
||||
layoutDirection == Direction::RTL &&
|
||||
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Start)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Right)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Right)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Right)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Horizontal)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
|
||||
} else {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::All)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
Style::Length computeBottomEdge(const Edges& edges) const {
|
||||
if (edges[yoga::to_underlying(Edge::Bottom)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Bottom)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Bottom)]);
|
||||
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::Vertical)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
|
||||
} else {
|
||||
return (Style::Length)edges[yoga::to_underlying(Edge::All)];
|
||||
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,19 +652,23 @@ class YG_EXPORT Style {
|
||||
Overflow overflow_ : bitCount<Overflow>() = Overflow::Visible;
|
||||
Display display_ : bitCount<Display>() = Display::Flex;
|
||||
|
||||
FloatOptional flex_{};
|
||||
FloatOptional flexGrow_{};
|
||||
FloatOptional flexShrink_{};
|
||||
CompactValue flexBasis_{CompactValue::ofAuto()};
|
||||
StyleValueHandle flex_{};
|
||||
StyleValueHandle flexGrow_{};
|
||||
StyleValueHandle flexShrink_{};
|
||||
StyleValueHandle flexBasis_{StyleValueHandle::ofAuto()};
|
||||
Edges margin_{};
|
||||
Edges position_{};
|
||||
Edges padding_{};
|
||||
Edges border_{};
|
||||
Gutters gap_{};
|
||||
Dimensions dimensions_{CompactValue::ofAuto(), CompactValue::ofAuto()};
|
||||
Dimensions dimensions_{
|
||||
StyleValueHandle::ofAuto(),
|
||||
StyleValueHandle::ofAuto()};
|
||||
Dimensions minDimensions_{};
|
||||
Dimensions maxDimensions_{};
|
||||
FloatOptional aspectRatio_{};
|
||||
StyleValueHandle aspectRatio_{};
|
||||
|
||||
StyleValuePool pool_;
|
||||
};
|
||||
|
||||
} // namespace facebook::yoga
|
||||
|
Reference in New Issue
Block a user