From 31a9e7e25ef2b4aff038334379389975e775c0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?= Date: Thu, 3 Sep 2026 04:54:56 -0700 Subject: [PATCH] Fix per-node memory regression caused by Grid styles Summary: # Why Currently, grid style properties are stored in yoga style (`gridTemplateRows_`, `gridAutoColumns_` etc). These properties increase the size of style object from `152` bytes to `280` bytes (84% increase). The cost is added even when a node is not a grid container or a grid item. # How Move grid style properties behind a pointer that is lazily allocated, on the first grid property set. A node that doesn't use grid only adds the cost of this pointer (8 bytes). So style now costs 160 bytes (5% increase). The public API remains unchanged. # Tests A test is added to catch the style size regression and `tests/GridStyleTest.cpp` includes additional cases to assert unset style, copy and move behaviour. X-link: https://github.com/react/yoga/pull/2018 Differential Revision: D118628661 Pulled By: javache --- .../ReactCommon/yoga/yoga/style/GridStyle.h | 80 +++++++++++++++++++ .../ReactCommon/yoga/yoga/style/Style.h | 69 +++++++--------- 2 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 packages/react-native/ReactCommon/yoga/yoga/style/GridStyle.h diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/GridStyle.h b/packages/react-native/ReactCommon/yoga/yoga/style/GridStyle.h new file mode 100644 index 000000000000..f1cf9d7b11bf --- /dev/null +++ b/packages/react-native/ReactCommon/yoga/yoga/style/GridStyle.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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 + +#include +#include + +namespace facebook::yoga { + +/** + * The CSS Grid properties of a single node. + */ +struct GridStyle { + // Grid container properties + GridTrackList templateColumns{}; + GridTrackList templateRows{}; + GridTrackList autoColumns{}; + GridTrackList autoRows{}; + + // Grid item properties + GridLine columnStart{}; + GridLine columnEnd{}; + GridLine rowStart{}; + GridLine rowEnd{}; + + bool operator==(const GridStyle& other) const = default; +}; + +/** + * Storage for a GridStyle which stays empty until the first grid property is + * set. + */ +class GridStyleStorage { + public: + GridStyleStorage() = default; + GridStyleStorage(GridStyleStorage&&) noexcept = default; + GridStyleStorage& operator=(GridStyleStorage&&) noexcept = default; + + GridStyleStorage(const GridStyleStorage& other) { + *this = other; + } + + GridStyleStorage& operator=(const GridStyleStorage& other) { + grid_ = other.grid_ == nullptr ? nullptr + : std::make_unique(*other.grid_); + return *this; + } + + const GridStyle& get() const { + return grid_ == nullptr ? defaults() : *grid_; + } + + GridStyle& ensure() { + if (grid_ == nullptr) { + grid_ = std::make_unique(); + } + return *grid_; + } + + bool operator==(const GridStyleStorage& other) const { + return grid_ == other.grid_ || get() == other.get(); + } + + private: + static const GridStyle& defaults() { + static const GridStyle kDefaults{}; + return kDefaults; + } + + std::unique_ptr grid_{}; +}; + +} // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/Style.h b/packages/react-native/ReactCommon/yoga/yoga/style/Style.h index a06bd246b456..00af38c692b4 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/Style.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/Style.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -209,84 +210,84 @@ class YG_EXPORT Style { // Grid Container Properties const GridTrackList& gridTemplateColumns() const { - return gridTemplateColumns_; + return grid_.get().templateColumns; } void setGridTemplateColumns(GridTrackList value) { - gridTemplateColumns_ = std::move(value); + grid_.ensure().templateColumns = std::move(value); } void resizeGridTemplateColumns(size_t count) { - gridTemplateColumns_.resize(count); + grid_.ensure().templateColumns.resize(count); } void setGridTemplateColumnAt(size_t index, GridTrackSize value) { - gridTemplateColumns_[index] = value; + grid_.ensure().templateColumns[index] = value; } const GridTrackList& gridTemplateRows() const { - return gridTemplateRows_; + return grid_.get().templateRows; } void setGridTemplateRows(GridTrackList value) { - gridTemplateRows_ = std::move(value); + grid_.ensure().templateRows = std::move(value); } void resizeGridTemplateRows(size_t count) { - gridTemplateRows_.resize(count); + grid_.ensure().templateRows.resize(count); } void setGridTemplateRowAt(size_t index, GridTrackSize value) { - gridTemplateRows_[index] = value; + grid_.ensure().templateRows[index] = value; } const GridTrackList& gridAutoColumns() const { - return gridAutoColumns_; + return grid_.get().autoColumns; } void setGridAutoColumns(GridTrackList value) { - gridAutoColumns_ = std::move(value); + grid_.ensure().autoColumns = std::move(value); } void resizeGridAutoColumns(size_t count) { - gridAutoColumns_.resize(count); + grid_.ensure().autoColumns.resize(count); } void setGridAutoColumnAt(size_t index, GridTrackSize value) { - gridAutoColumns_[index] = value; + grid_.ensure().autoColumns[index] = value; } const GridTrackList& gridAutoRows() const { - return gridAutoRows_; + return grid_.get().autoRows; } void setGridAutoRows(GridTrackList value) { - gridAutoRows_ = std::move(value); + grid_.ensure().autoRows = std::move(value); } void resizeGridAutoRows(size_t count) { - gridAutoRows_.resize(count); + grid_.ensure().autoRows.resize(count); } void setGridAutoRowAt(size_t index, GridTrackSize value) { - gridAutoRows_[index] = value; + grid_.ensure().autoRows[index] = value; } // Grid Item Properties const GridLine& gridColumnStart() const { - return gridColumnStart_; + return grid_.get().columnStart; } void setGridColumnStart(GridLine value) { - gridColumnStart_ = value; + grid_.ensure().columnStart = value; } const GridLine& gridColumnEnd() const { - return gridColumnEnd_; + return grid_.get().columnEnd; } void setGridColumnEnd(GridLine value) { - gridColumnEnd_ = value; + grid_.ensure().columnEnd = value; } const GridLine& gridRowStart() const { - return gridRowStart_; + return grid_.get().rowStart; } void setGridRowStart(GridLine value) { - gridRowStart_ = value; + grid_.ensure().rowStart = value; } const GridLine& gridRowEnd() const { - return gridRowEnd_; + return grid_.get().rowEnd; } void setGridRowEnd(GridLine value) { - gridRowEnd_ = value; + grid_.ensure().rowEnd = value; } FloatOptional resolvedMinDimension( @@ -667,14 +668,7 @@ class YG_EXPORT Style { sizeLengthsEqual( maxDimensions_, pool_, other.maxDimensions_, other.pool_) && numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_) && - gridTemplateColumns_ == other.gridTemplateColumns_ && - gridTemplateRows_ == other.gridTemplateRows_ && - gridAutoColumns_ == other.gridAutoColumns_ && - gridAutoRows_ == other.gridAutoRows_ && - gridColumnStart_ == other.gridColumnStart_ && - gridColumnEnd_ == other.gridColumnEnd_ && - gridRowStart_ == other.gridRowStart_ && - gridRowEnd_ == other.gridRowEnd_; + grid_ == other.grid_; } private: @@ -929,15 +923,8 @@ class YG_EXPORT Style { Dimensions maxDimensions_{}; StyleValueHandle aspectRatio_{}; - // Grid properties - GridTrackList gridTemplateColumns_{}; - GridTrackList gridTemplateRows_{}; - GridTrackList gridAutoColumns_{}; - GridTrackList gridAutoRows_{}; - GridLine gridColumnStart_{}; - GridLine gridColumnEnd_{}; - GridLine gridRowStart_{}; - GridLine gridRowEnd_{}; + // Grid properties, allocated only when one of them is set + GridStyleStorage grid_{}; StyleValuePool pool_; };