Skip to content

Commit 1fe79ee

Browse files
paradowstackfacebook-github-bot
authored andcommitted
Optimize style value resolution with handle-based approach
Summary: This PR is an optimization attempt during work on the `calc()` feature: facebook/yoga#1874. It aims to reduce the regression effect on layout operations after extending the size of `StyleLength` and `StyleSizeLength` classes. ## Changelog: This PR introduces a private `resolve(StyleValueHandle handle, float referenceLength)` function to `Style.h` that resolves its property values directly from `StyleValueHandle` and skips creating intermediate objects. I am aware that this duplicates the resolving functionality that already exists in `StyleLength.h` and `StyleSizeLength.h`, and may not be the cleanest solution. Optimization could go even further and totally drop the `resolve()` function from `StyleLength.h` and `StyleSizeLength.h`, and replace all usages with the newly introduced functions. But I think the performance boost is not high enough to justify that. Also it won't be the best API for Yoga library consumers - that's why I decided to keep them. However, I am opening this PR to start a discussion and would really like to hear opinions about it. ## Benchmarks: Results were averaged over 20 runs. This does not bring much gain in terms of performance when compared to the main branch. <img width="1366" height="254" alt="image" src="https://github.com/user-attachments/assets/19ec2a31-db60-46bc-84e1-5ad8eb9ddfcd" /> However, it introduces a significant boost for the calc() and YGValueDynamic work. Results before these optimizations: <img width="1390" height="276" alt="image" src="https://github.com/user-attachments/assets/68c17e88-78e2-439b-bf09-acb869d5533d" /> And after: <img width="1363" height="252" alt="image" src="https://github.com/user-attachments/assets/212b9d11-5e02-4524-b9ba-90b43130d02c" /> cc NickGerleman X-link: facebook/yoga#1922 Reviewed By: zeyap Differential Revision: D99006553 Pulled By: NickGerleman
1 parent 2bcb3e1 commit 1fe79ee

File tree

3 files changed

+117
-68
lines changed

3 files changed

+117
-68
lines changed

packages/react-native/ReactCommon/yoga/yoga/style/Style.h

Lines changed: 99 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,12 @@ class YG_EXPORT Style {
294294
Dimension axis,
295295
float referenceLength,
296296
float ownerWidth) const {
297-
FloatOptional value = minDimension(axis).resolve(referenceLength);
298-
if (boxSizing() == BoxSizing::BorderBox) {
297+
const auto handle = minDimensions_[yoga::to_underlying(axis)];
298+
if (handle.isUndefined()) {
299+
return FloatOptional{};
300+
}
301+
FloatOptional value = resolve(handle, referenceLength);
302+
if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) {
299303
return value;
300304
}
301305

@@ -319,8 +323,12 @@ class YG_EXPORT Style {
319323
Dimension axis,
320324
float referenceLength,
321325
float ownerWidth) const {
322-
FloatOptional value = maxDimension(axis).resolve(referenceLength);
323-
if (boxSizing() == BoxSizing::BorderBox) {
326+
const auto handle = maxDimensions_[yoga::to_underlying(axis)];
327+
if (handle.isUndefined()) {
328+
return FloatOptional{};
329+
}
330+
FloatOptional value = resolve(handle, referenceLength);
331+
if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) {
324332
return value;
325333
}
326334

@@ -409,99 +417,98 @@ class YG_EXPORT Style {
409417
FlexDirection axis,
410418
Direction direction,
411419
float axisSize) const {
412-
return computePosition(flexStartEdge(axis), direction)
413-
.resolve(axisSize)
420+
return resolve(computePosition(flexStartEdge(axis), direction), axisSize)
414421
.unwrapOrDefault(0.0f);
415422
}
416423

417424
float computeInlineStartPosition(
418425
FlexDirection axis,
419426
Direction direction,
420427
float axisSize) const {
421-
return computePosition(inlineStartEdge(axis, direction), direction)
422-
.resolve(axisSize)
428+
return resolve(
429+
computePosition(inlineStartEdge(axis, direction), direction),
430+
axisSize)
423431
.unwrapOrDefault(0.0f);
424432
}
425433

426434
float computeFlexEndPosition(
427435
FlexDirection axis,
428436
Direction direction,
429437
float axisSize) const {
430-
return computePosition(flexEndEdge(axis), direction)
431-
.resolve(axisSize)
438+
return resolve(computePosition(flexEndEdge(axis), direction), axisSize)
432439
.unwrapOrDefault(0.0f);
433440
}
434441

435442
float computeInlineEndPosition(
436443
FlexDirection axis,
437444
Direction direction,
438445
float axisSize) const {
439-
return computePosition(inlineEndEdge(axis, direction), direction)
440-
.resolve(axisSize)
446+
return resolve(
447+
computePosition(inlineEndEdge(axis, direction), direction),
448+
axisSize)
441449
.unwrapOrDefault(0.0f);
442450
}
443451

444452
float computeFlexStartMargin(
445453
FlexDirection axis,
446454
Direction direction,
447455
float widthSize) const {
448-
return computeMargin(flexStartEdge(axis), direction)
449-
.resolve(widthSize)
456+
return resolve(computeMargin(flexStartEdge(axis), direction), widthSize)
450457
.unwrapOrDefault(0.0f);
451458
}
452459

453460
float computeInlineStartMargin(
454461
FlexDirection axis,
455462
Direction direction,
456463
float widthSize) const {
457-
return computeMargin(inlineStartEdge(axis, direction), direction)
458-
.resolve(widthSize)
464+
return resolve(
465+
computeMargin(inlineStartEdge(axis, direction), direction),
466+
widthSize)
459467
.unwrapOrDefault(0.0f);
460468
}
461469

462470
float computeFlexEndMargin(
463471
FlexDirection axis,
464472
Direction direction,
465473
float widthSize) const {
466-
return computeMargin(flexEndEdge(axis), direction)
467-
.resolve(widthSize)
474+
return resolve(computeMargin(flexEndEdge(axis), direction), widthSize)
468475
.unwrapOrDefault(0.0f);
469476
}
470477

471478
float computeInlineEndMargin(
472479
FlexDirection axis,
473480
Direction direction,
474481
float widthSize) const {
475-
return computeMargin(inlineEndEdge(axis, direction), direction)
476-
.resolve(widthSize)
482+
return resolve(
483+
computeMargin(inlineEndEdge(axis, direction), direction),
484+
widthSize)
477485
.unwrapOrDefault(0.0f);
478486
}
479487

480488
float computeFlexStartBorder(FlexDirection axis, Direction direction) const {
481489
return maxOrDefined(
482-
computeBorder(flexStartEdge(axis), direction).resolve(0.0f).unwrap(),
490+
resolve(computeBorder(flexStartEdge(axis), direction), 0.0f).unwrap(),
483491
0.0f);
484492
}
485493

486494
float computeInlineStartBorder(FlexDirection axis, Direction direction)
487495
const {
488496
return maxOrDefined(
489-
computeBorder(inlineStartEdge(axis, direction), direction)
490-
.resolve(0.0f)
497+
resolve(
498+
computeBorder(inlineStartEdge(axis, direction), direction), 0.0f)
491499
.unwrap(),
492500
0.0f);
493501
}
494502

495503
float computeFlexEndBorder(FlexDirection axis, Direction direction) const {
496504
return maxOrDefined(
497-
computeBorder(flexEndEdge(axis), direction).resolve(0.0f).unwrap(),
505+
resolve(computeBorder(flexEndEdge(axis), direction), 0.0f).unwrap(),
498506
0.0f);
499507
}
500508

501509
float computeInlineEndBorder(FlexDirection axis, Direction direction) const {
502510
return maxOrDefined(
503-
computeBorder(inlineEndEdge(axis, direction), direction)
504-
.resolve(0.0f)
511+
resolve(computeBorder(inlineEndEdge(axis, direction), direction), 0.0f)
505512
.unwrap(),
506513
0.0f);
507514
}
@@ -511,8 +518,7 @@ class YG_EXPORT Style {
511518
Direction direction,
512519
float widthSize) const {
513520
return maxOrDefined(
514-
computePadding(flexStartEdge(axis), direction)
515-
.resolve(widthSize)
521+
resolve(computePadding(flexStartEdge(axis), direction), widthSize)
516522
.unwrap(),
517523
0.0f);
518524
}
@@ -522,8 +528,9 @@ class YG_EXPORT Style {
522528
Direction direction,
523529
float widthSize) const {
524530
return maxOrDefined(
525-
computePadding(inlineStartEdge(axis, direction), direction)
526-
.resolve(widthSize)
531+
resolve(
532+
computePadding(inlineStartEdge(axis, direction), direction),
533+
widthSize)
527534
.unwrap(),
528535
0.0f);
529536
}
@@ -533,8 +540,7 @@ class YG_EXPORT Style {
533540
Direction direction,
534541
float widthSize) const {
535542
return maxOrDefined(
536-
computePadding(flexEndEdge(axis), direction)
537-
.resolve(widthSize)
543+
resolve(computePadding(flexEndEdge(axis), direction), widthSize)
538544
.unwrap(),
539545
0.0f);
540546
}
@@ -544,8 +550,9 @@ class YG_EXPORT Style {
544550
Direction direction,
545551
float widthSize) const {
546552
return maxOrDefined(
547-
computePadding(inlineEndEdge(axis, direction), direction)
548-
.resolve(widthSize)
553+
resolve(
554+
computePadding(inlineEndEdge(axis, direction), direction),
555+
widthSize)
549556
.unwrap(),
550557
0.0f);
551558
}
@@ -610,13 +617,13 @@ class YG_EXPORT Style {
610617

611618
float computeGapForAxis(FlexDirection axis, float ownerSize) const {
612619
auto gap = isRow(axis) ? computeColumnGap() : computeRowGap();
613-
return maxOrDefined(gap.resolve(ownerSize).unwrap(), 0.0f);
620+
return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f);
614621
}
615622

616623
float computeGapForDimension(Dimension dimension, float ownerSize) const {
617624
auto gap =
618625
dimension == Dimension::Width ? computeColumnGap() : computeRowGap();
619-
return maxOrDefined(gap.resolve(ownerSize).unwrap(), 0.0f);
626+
return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f);
620627
}
621628

622629
bool flexStartMarginIsAuto(FlexDirection axis, Direction direction) const {
@@ -709,79 +716,82 @@ class YG_EXPORT Style {
709716
});
710717
}
711718

712-
Style::Length computeColumnGap() const {
719+
StyleValueHandle computeColumnGap() const {
713720
if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) {
714-
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Column)]);
721+
return gap_[yoga::to_underlying(Gutter::Column)];
715722
} else {
716-
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
723+
return gap_[yoga::to_underlying(Gutter::All)];
717724
}
718725
}
719726

720-
Style::Length computeRowGap() const {
727+
StyleValueHandle computeRowGap() const {
721728
if (gap_[yoga::to_underlying(Gutter::Row)].isDefined()) {
722-
return pool_.getLength(gap_[yoga::to_underlying(Gutter::Row)]);
729+
return gap_[yoga::to_underlying(Gutter::Row)];
723730
} else {
724-
return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]);
731+
return gap_[yoga::to_underlying(Gutter::All)];
725732
}
726733
}
727734

728-
Style::Length computeLeftEdge(const Edges& edges, Direction layoutDirection)
729-
const {
735+
StyleValueHandle computeLeftEdge(
736+
const Edges& edges,
737+
Direction layoutDirection) const {
730738
if (layoutDirection == Direction::LTR &&
731739
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
732-
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
740+
return edges[yoga::to_underlying(Edge::Start)];
733741
} else if (
734742
layoutDirection == Direction::RTL &&
735743
edges[yoga::to_underlying(Edge::End)].isDefined()) {
736-
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
744+
return edges[yoga::to_underlying(Edge::End)];
737745
} else if (edges[yoga::to_underlying(Edge::Left)].isDefined()) {
738-
return pool_.getLength(edges[yoga::to_underlying(Edge::Left)]);
746+
return edges[yoga::to_underlying(Edge::Left)];
739747
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
740-
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
748+
return edges[yoga::to_underlying(Edge::Horizontal)];
741749
} else {
742-
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
750+
return edges[yoga::to_underlying(Edge::All)];
743751
}
744752
}
745753

746-
Style::Length computeTopEdge(const Edges& edges) const {
754+
StyleValueHandle computeTopEdge(const Edges& edges) const {
747755
if (edges[yoga::to_underlying(Edge::Top)].isDefined()) {
748-
return pool_.getLength(edges[yoga::to_underlying(Edge::Top)]);
756+
return edges[yoga::to_underlying(Edge::Top)];
749757
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
750-
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
758+
return edges[yoga::to_underlying(Edge::Vertical)];
751759
} else {
752-
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
760+
return edges[yoga::to_underlying(Edge::All)];
753761
}
754762
}
755763

756-
Style::Length computeRightEdge(const Edges& edges, Direction layoutDirection)
757-
const {
764+
StyleValueHandle computeRightEdge(
765+
const Edges& edges,
766+
Direction layoutDirection) const {
758767
if (layoutDirection == Direction::LTR &&
759768
edges[yoga::to_underlying(Edge::End)].isDefined()) {
760-
return pool_.getLength(edges[yoga::to_underlying(Edge::End)]);
769+
return edges[yoga::to_underlying(Edge::End)];
761770
} else if (
762771
layoutDirection == Direction::RTL &&
763772
edges[yoga::to_underlying(Edge::Start)].isDefined()) {
764-
return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]);
773+
return edges[yoga::to_underlying(Edge::Start)];
765774
} else if (edges[yoga::to_underlying(Edge::Right)].isDefined()) {
766-
return pool_.getLength(edges[yoga::to_underlying(Edge::Right)]);
775+
return edges[yoga::to_underlying(Edge::Right)];
767776
} else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) {
768-
return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]);
777+
return edges[yoga::to_underlying(Edge::Horizontal)];
769778
} else {
770-
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
779+
return edges[yoga::to_underlying(Edge::All)];
771780
}
772781
}
773782

774-
Style::Length computeBottomEdge(const Edges& edges) const {
783+
StyleValueHandle computeBottomEdge(const Edges& edges) const {
775784
if (edges[yoga::to_underlying(Edge::Bottom)].isDefined()) {
776-
return pool_.getLength(edges[yoga::to_underlying(Edge::Bottom)]);
785+
return edges[yoga::to_underlying(Edge::Bottom)];
777786
} else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) {
778-
return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]);
787+
return edges[yoga::to_underlying(Edge::Vertical)];
779788
} else {
780-
return pool_.getLength(edges[yoga::to_underlying(Edge::All)]);
789+
return edges[yoga::to_underlying(Edge::All)];
781790
}
782791
}
783792

784-
Style::Length computePosition(PhysicalEdge edge, Direction direction) const {
793+
StyleValueHandle computePosition(PhysicalEdge edge, Direction direction)
794+
const {
785795
switch (edge) {
786796
case PhysicalEdge::Left:
787797
return computeLeftEdge(position_, direction);
@@ -796,7 +806,7 @@ class YG_EXPORT Style {
796806
}
797807
}
798808

799-
Style::Length computeMargin(PhysicalEdge edge, Direction direction) const {
809+
StyleValueHandle computeMargin(PhysicalEdge edge, Direction direction) const {
800810
switch (edge) {
801811
case PhysicalEdge::Left:
802812
return computeLeftEdge(margin_, direction);
@@ -811,7 +821,8 @@ class YG_EXPORT Style {
811821
}
812822
}
813823

814-
Style::Length computePadding(PhysicalEdge edge, Direction direction) const {
824+
StyleValueHandle computePadding(PhysicalEdge edge, Direction direction)
825+
const {
815826
switch (edge) {
816827
case PhysicalEdge::Left:
817828
return computeLeftEdge(padding_, direction);
@@ -826,7 +837,7 @@ class YG_EXPORT Style {
826837
}
827838
}
828839

829-
Style::Length computeBorder(PhysicalEdge edge, Direction direction) const {
840+
StyleValueHandle computeBorder(PhysicalEdge edge, Direction direction) const {
830841
switch (edge) {
831842
case PhysicalEdge::Left:
832843
return computeLeftEdge(border_, direction);
@@ -841,6 +852,26 @@ class YG_EXPORT Style {
841852
}
842853
}
843854

855+
/**
856+
* Internal resolution of a StyleValueHandle.
857+
*
858+
* Part of the handle-based optimization, this function allows the layout
859+
* engine to resolve stored values (Points, Percents) directly from the pool
860+
* via handles. This avoids the overhead of materializing an intermediate
861+
* StyleLength/StyleSizeLength object on the stack during hot-path overhead
862+
* calculations.
863+
*/
864+
FloatOptional resolve(StyleValueHandle handle, float referenceLength) const {
865+
if (handle.isPoint()) {
866+
return FloatOptional{pool_.getStoredValue(handle)};
867+
}
868+
if (handle.isPercent()) {
869+
return FloatOptional{
870+
pool_.getStoredValue(handle) * referenceLength * 0.01f};
871+
}
872+
return FloatOptional{};
873+
}
874+
844875
Direction direction_ : bitCount<Direction>() = Direction::Inherit;
845876
FlexDirection flexDirection_
846877
: bitCount<FlexDirection>() = FlexDirection::Column;

packages/react-native/ReactCommon/yoga/yoga/style/StyleValueHandle.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ class StyleValueHandle {
4949
return type() == Type::Auto;
5050
}
5151

52+
constexpr bool isPercent() const {
53+
return type() == Type::Percent;
54+
}
55+
56+
constexpr bool isPoint() const {
57+
return type() == Type::Point;
58+
}
59+
5260
private:
5361
friend class StyleValuePool;
5462

0 commit comments

Comments
 (0)