Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(ivy): apply [style]/[class] bindings directly to style/className #33336

Closed
wants to merge 2 commits into from

Conversation

matsko
Copy link
Contributor

@matsko matsko commented Oct 22, 2019

This patch ensures that the [style] and [class] based bindings
are directly applied to an element's style and className attributes.

This patch optimizes the algorithm so that it...

  • Doesn't construct an update an instance of StylingMapArray for
    [style] and [class] bindings
  • Doesn't apply [style] and [class] based entries using
    classList and style (direct attributes are used instead)
  • Doesn't split or iterate over all string-based tokens in a
    string value obtained from a [class] binding.

This patch speeds up the following cases:

  • <div [class]> and <div class="..." [class]>
  • <div [style]> and <div style="..." [style]>

The overall speed increase is by over 5x.

@matsko matsko added target: major This PR is targeted for the next major release comp: ivy labels Oct 22, 2019
@ngbot ngbot bot modified the milestone: needsTriage Oct 22, 2019
@matsko matsko changed the title perf(ivy): apply [class] bindings directly to className perf(ivy): apply [style]/[class] bindings directly to style/className Oct 22, 2019
@matsko
Copy link
Contributor Author

matsko commented Oct 23, 2019

packages/core/src/render3/instructions/styling.ts Outdated Show resolved Hide resolved
packages/core/src/render3/instructions/styling.ts Outdated Show resolved Hide resolved
packages/core/src/render3/styling/bindings.ts Outdated Show resolved Hide resolved
valueToApply += forceStylesAsString(value as{[key: string]: any});
setStyleAttr(renderer, element, valueToApply);
}
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Instead of return true convert this into if () { } else {} block.
  2. Then extract the else block into a separate function. This makes applyStylingMapDirectly smaller and more inlinable (this improves performance about 10%).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

packages/core/src/render3/instructions/styling.ts Outdated Show resolved Hide resolved
@matsko matsko marked this pull request as ready for review October 23, 2019 07:24
@matsko matsko requested a review from a team as a code owner October 23, 2019 07:24
@matsko matsko force-pushed the class_optimizations branch 3 times, most recently from 71a29db to e40ea58 Compare October 23, 2019 07:42
@IgorMinar
Copy link
Contributor

@IgorMinar
Copy link
Contributor

guitar presubmits are failing with some styling errors: http://fusion/2cb9bb68-1356-421b-8d6c-550c700bb15b

Copy link
Contributor

@mhevery mhevery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

  • Let's land this PR as is.
  • I think there are a lot of micro-improvements we could do. Let's do that once we have time.

@matsko matsko force-pushed the class_optimizations branch 2 times, most recently from 0a44db0 to 416aa5e Compare October 24, 2019 04:39
@matsko matsko requested a review from a team as a code owner October 24, 2019 04:39
@matsko matsko force-pushed the class_optimizations branch 4 times, most recently from 3801c3b to 8371baf Compare October 24, 2019 05:34
@matsko
Copy link
Contributor Author

matsko commented Oct 24, 2019

@matsko
Copy link
Contributor Author

matsko commented Oct 24, 2019

// className / style value must be compared against.
const increment = isMapBased ? 2 : 1;
const index = lView[BINDING_INDEX] += increment;
return index - increment;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rewrite this in a way which does not require both adding and subtracting the increment

if (cachedValue === VALUE_IS_EXTERNALLY_MODIFIED) return true;

// web workers are not covered in this case
if (typeof Element === 'undefined' || !(element instanceof Element)) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you pull this out of this function. No need to execute `typeof Element === 'undefined' all the time.

const NO_ELEMENT = typeof Element === 'undefined' ;


// comparing the DOM value against the cached value is the best way to
// see if something has changed.
const e = element as HTMLElement;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could type the argument of this function as HTMLElement element (since you are already casting it to HTMLElement at the invocation place.

// comparing the DOM value against the cached value is the best way to
// see if something has changed.
const e = element as HTMLElement;
const currentValue = isClassBased ? e.className : (e.style && e.style.cssText);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, while I'm curious how it is going to perform, I'm also curious on how it is going to play with other renderers (ex. NativeScript). Other renderers might have a concept of a "class" and "style" but not necessarily store it at the className / style property.

As the very minimum we should probably guard it with a check for the existence of this property...

@matsko matsko force-pushed the class_optimizations branch 4 times, most recently from 72aab4d to 6df1a2f Compare October 24, 2019 22:15
This patch ensures that the `[style]` and `[class]` based bindings
are directly applied to an element's style and className attributes.

This patch optimizes the algorithm so that it...
- Doesn't construct an update an instance of `StylingMapArray` for
  `[style]` and `[class]` bindings
- Doesn't apply `[style]` and `[class]` based entries using
  `classList` and `style` (direct attributes are used instead)
- Doesn't split or iterate over all string-based tokens in a
  string value obtained from a `[class]` binding.

This patch speeds up the following cases:
- `<div [class]>` and `<div class="..." [class]>`
- `<div [style]>` and `<div style="..." [style]>`

The overall speec increase is by over 5x.
@matsko matsko added the action: merge The PR is ready for merge by the caretaker label Oct 25, 2019
@ngbot
Copy link

ngbot bot commented Oct 25, 2019

I see that you just added the PR action: merge label, but the following checks are still failing:
    failure status "google3" is failing
    pending 1 pending code review

If you want your PR to be merged, it has to pass all the CI checks.

If you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help.

@matsko
Copy link
Contributor Author

matsko commented Oct 25, 2019

merge-assistance: approved by misko and approved over slack by Pawel

@matsko matsko added merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note and removed action: merge The PR is ready for merge by the caretaker labels Oct 25, 2019
@ngbot ngbot bot added the action: merge The PR is ready for merge by the caretaker label Oct 25, 2019
@ngbot
Copy link

ngbot bot commented Oct 25, 2019

I see that you just added the PR action: merge label, but the following checks are still failing:
    failure status "google3" is failing
    pending 1 pending code review

If you want your PR to be merged, it has to pass all the CI checks.

If you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help.

@AndrewKushnir
Copy link
Contributor

Most recent presubmits (for history):

Presubmits are successful.

mhevery pushed a commit to mhevery/angular that referenced this pull request Oct 25, 2019
…angular#33336)

This patch ensures that the `[style]` and `[class]` based bindings
are directly applied to an element's style and className attributes.

This patch optimizes the algorithm so that it...
- Doesn't construct an update an instance of `StylingMapArray` for
  `[style]` and `[class]` bindings
- Doesn't apply `[style]` and `[class]` based entries using
  `classList` and `style` (direct attributes are used instead)
- Doesn't split or iterate over all string-based tokens in a
  string value obtained from a `[class]` binding.

This patch speeds up the following cases:
- `<div [class]>` and `<div class="..." [class]>`
- `<div [style]>` and `<div style="..." [style]>`

The overall speec increase is by over 5x.

PR Close angular#33336
// that are no longer active on the element.
return createProxy({
get(target: {}, prop: string): DebugNodeStylingEntry{
let value: DebugNodeStylingEntry = entries[prop]; if (!value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The position of if looks weird.

pkozlowski-opensource pushed a commit to pkozlowski-opensource/angular that referenced this pull request Oct 25, 2019
…angular#33336)

This patch ensures that the `[style]` and `[class]` based bindings
are directly applied to an element's style and className attributes.

This patch optimizes the algorithm so that it...
- Doesn't construct an update an instance of `StylingMapArray` for
  `[style]` and `[class]` bindings
- Doesn't apply `[style]` and `[class]` based entries using
  `classList` and `style` (direct attributes are used instead)
- Doesn't split or iterate over all string-based tokens in a
  string value obtained from a `[class]` binding.

This patch speeds up the following cases:
- `<div [class]>` and `<div class="..." [class]>`
- `<div [style]>` and `<div style="..." [style]>`

The overall speec increase is by over 5x.

PR Close angular#33336
@matsko matsko deleted the class_optimizations branch October 25, 2019 17:08
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Nov 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants