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

Optimized filterDOMProps. #939

Merged
merged 2 commits into from
Apr 28, 2021
Merged

Optimized filterDOMProps. #939

merged 2 commits into from
Apr 28, 2021

Conversation

radekmie
Copy link
Contributor

@radekmie radekmie commented Apr 27, 2021

TL;DR; filterDOMProps is twice as fast now!

This PR optimizes filterDOMProps. The benchmark I created consisted of two parts: one where 70%-100% of props were removed and the other where only 0%-20% were removed. Based on one of our apps, an average % of removed props is ~76%.

Additionally, the new version results in a smaller bundle as no lodash functions are used.

And finally, I rearranged the code a little to make sure that it'll appear as filterDOMProps instead of filter in error and profiler traces.


921ms + 394ms = 1315ms
function filter<T extends object>(props: T) {
  return pickBy(props, filterOne) as Omit<T, FilterDOMPropsKeys>;
}
606ms + 166ms = 772ms
function filter<T extends object>(props: T) {
  const filteredProps = { ...props };
  for (let index = 0; index < registered.length; ++index) {
    const prop = registered[index];
    if (prop in filteredProps) {
      delete (filteredProps as any)[prop];
    }
  }

  return filteredProps as Omit<T, FilterDOMPropsKeys>;
}
602ms + 47ms = 649ms
function filter<T extends object>(props: T) {
  const filteredProps = { ...props };
  for (const prop in props) {
    if (registered.includes(prop)) {
      delete filteredProps[prop];
    }
  }

  return filteredProps as Omit<T, FilterDOMPropsKeys>;
}
552ms + 40ms = 592ms
function filter<T extends object>(props: T) {
  const filteredProps = { ...props };
  for (const prop in props) {
    if (registeredCache.has(prop)) {
      delete filteredProps[prop];
    }
  }

  return filteredProps as Omit<T, FilterDOMPropsKeys>;
}
Benchmarks
describe.only('benchmark A', () => {
  it.each(Array.from({ length: 25 }, (_, x) => x + 1))('is fast %i', () => {
    expect(true).toBe(true);
    for (let index = 0; index < 5000; ++index) {
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, v: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, va: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, val: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, valu: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, value: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, c: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, ch: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, cha: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, chan: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, chang: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, change: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, changed: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, v: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, va: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, val: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, valu: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, value: 1 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, c: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, ch: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, cha: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, chan: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, chang: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, change: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, changed: 2 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, v: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, va: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, val: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, valu: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, value: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, c: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, ch: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, cha: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, chan: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, chang: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, change: 3 });
      filterDOMProps({ allowedValues: 1, component: 1, disabled: 1, error: 1, fieldType: 1, fields: 1, initialCount: 1, label: 1, name: 1, onChange: 1, placeholder: 1, showInlineError: 1, transform: 1, a: 1, b: 2, changed: 3 });
    }
  });
});

describe.only('benchmark B', () => {
  it.each(Array.from({ length: 25 }, (_, x) => x + 1))('is fast %i', () => {
    expect(true).toBe(true);
    for (let index = 0; index < 5000; ++index) {
      filterDOMProps({ v: 1 });
      filterDOMProps({ va: 1 });
      filterDOMProps({ val: 1 });
      filterDOMProps({ valu: 1 });
      filterDOMProps({ value: 1 });
      filterDOMProps({ c: 1 });
      filterDOMProps({ ch: 1 });
      filterDOMProps({ cha: 1 });
      filterDOMProps({ chan: 1 });
      filterDOMProps({ chang: 1 });
      filterDOMProps({ change: 1 });
      filterDOMProps({ changed: 1 });
      filterDOMProps({ a: 1, v: 1 });
      filterDOMProps({ a: 1, va: 1 });
      filterDOMProps({ a: 1, val: 1 });
      filterDOMProps({ a: 1, valu: 1 });
      filterDOMProps({ a: 1, value: 1 });
      filterDOMProps({ a: 1, c: 2 });
      filterDOMProps({ a: 1, ch: 2 });
      filterDOMProps({ a: 1, cha: 2 });
      filterDOMProps({ a: 1, chan: 2 });
      filterDOMProps({ a: 1, chang: 2 });
      filterDOMProps({ a: 1, change: 2 });
      filterDOMProps({ a: 1, changed: 2 });
      filterDOMProps({ a: 1, b: 2, v: 3 });
      filterDOMProps({ a: 1, b: 2, va: 3 });
      filterDOMProps({ a: 1, b: 2, val: 3 });
      filterDOMProps({ a: 1, b: 2, valu: 3 });
      filterDOMProps({ a: 1, b: 2, value: 3 });
      filterDOMProps({ a: 1, b: 2, c: 3 });
      filterDOMProps({ a: 1, b: 2, ch: 3 });
      filterDOMProps({ a: 1, b: 2, cha: 3 });
      filterDOMProps({ a: 1, b: 2, chan: 3 });
      filterDOMProps({ a: 1, b: 2, chang: 3 });
      filterDOMProps({ a: 1, b: 2, change: 3 });
      filterDOMProps({ a: 1, b: 2, changed: 3 });
    }
  });
});

@radekmie radekmie added the Type: Feature New features and feature requests label Apr 27, 2021
@codecov
Copy link

codecov bot commented Apr 27, 2021

Codecov Report

Merging #939 (53c43b5) into master (6a21f52) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #939   +/-   ##
=======================================
  Coverage   97.48%   97.48%           
=======================================
  Files         202      202           
  Lines        3938     3941    +3     
  Branches      809      810    +1     
=======================================
+ Hits         3839     3842    +3     
  Misses         11       11           
  Partials       88       88           
Impacted Files Coverage Δ
packages/uniforms/src/filterDOMProps.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6a21f52...53c43b5. Read the comment docs.

@radekmie radekmie merged commit 7f1fddb into master Apr 28, 2021
@radekmie radekmie deleted the optimized-filterdomprops branch April 28, 2021 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Feature New features and feature requests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants