Performance Tips

July 13th, 2018

JavaScript

Big-O notation

$$ f(n) = O(n) $$

As \( n \to \infty \), the time ( \( f(n) \) ) a function takes takes to execute is bounded above by \(c \cdot n \)

More Formally \( f(n) = O(n) \) means...

$$ f(n) \leq c \times n $$

for all \( n > N \)

where \( N \) is a really big number

and \(c \) is some number we choose

Title Text

function sum(arr: number[]) {
  let out = 0;

  for (const val of arr) {
    out += 1;
    out += val;
  }

  return out;
}

What is \( f(n) \)?

1 operation

\(n\) values in arr

1 operation

1 operation

loops \(n \) times

\( f(n) = 2 + 2n \)

Time Taken ( \( f(n)\) )

\( n \)

\( f(n) = 2 + 2n \) 

\( f(n) = n \) 

\( \infty \) 

\( \infty \) 

\( f(n) = 6n \) 

\( f(n)= 3 + 2n^2\)

\( f(n) = O(n^2) \)

\( g(n) = 3 + 2n \)

\( n \)

Time Taken ( \( f(n)\) )

Title Text

function hasPairWithSum(arr: number[], sum: number) {
  for (const a of arr) {
    for (const b of arr) {
        if (a + b === sum) {
            return true; 
        }
    }
  }

  return false;
}

What is \( f(n) \)?

\(n\) values in arr

loops \(n \) times

What is \( f(n) \)?

function hasPairWithSum(arr: number[], sum: number) {
  for (const a of arr) {
    const need = sum - a;
    if (has(arr, need)) {
      return true;
    }
  }

  return false;
}

loops \(n \) times

?

What is \( f(n) \)?

const has1 = (arr, n) => arr.indexOf(n) !== -1;
const has2 = (arr, n) => arr.find(x => n === x);

// assume arr is sorted
const has3 = (arr, n) => {
  let low = 0;
  let high = arr.length - 1;

  while (low < high) {
    const mid = Math.floor(low + high);
    if (arr[mid] === n) return true;
    if (arr[mid] > n) high = mid - 1;
    if (arr[mid] < n) low = mid + 1;
  }

  return false;
};

What is \( f(n) \)?

const cache = new WeakMap();

function has4(arr: number[], sum: number) {
    if (cache.has(arr)) return cache.get(arr).has(n);

    const lookup = new Set(arr);

    cache.set(arr, lookup);

    return lookup.has(n);
}

Array Methods

  • arr.indexOf()
  • arr.find()
  • arr.map()
  • arr.reduce()
  • arr.concat()
  • arr.shift()
  • arr.unshift()
  • arr.slice()

Others

  • Object.assign
  • { ...obj }
  • [...arr, ...arr]
arr.reduce((out, val) => {
  return { ...out, [val]: true };
}, {});

Memoization

fancy word for caching

Title Text

// 1, 1, 2, 3, 5, 8, 13, ...

function fib(n) {
    if (n === 1) return 1;
    if (n === 2) return 1;

    return fib(n - 1) + fib(n - 2)
}

What is \( f(n) \)?

Title Text

// 1, 1, 2, 3, 5, 8, 13, ...

function fibHelper(n) {
    if (n === 1) return 1;
    if (n === 2) return 1;

    return fib(n - 1) + fib(n - 2)
}

const fib = (() => {
    const cache = new Map();
    
    return (n) => {
        if (cache.has(n)) return cache.get(n);
        const a = fibHelper(n - 1);
        const b = fibHelper(n - 2);
        const result = a + b;

        cache.set(n, result);
        return result;  
    };
})();

What is \( f(n) \)?

/**
 * memoize function using weakmap
 */
export function weakMemoize<K extends object, V>(fn: (key: K) => V) {
  const cache = new WeakMap<K, V>();
  return (key: K) => {
    const cached = cache.get(key);
    if (cached) {
      return cached;
    }

    const value = fn(key);
    cache.set(key, value);
    return value;
  };
}



declare function expensive(arr: Task[]): number;

const faster = weakMemoize(expensive);

faster(arr); // -> number;

React

shouldComponentUpdate()

class Task extends React.Component {
  
  shouldComponentUpdate(newProps) {
      for (const key in newProps) {
          if (newProps[key] !== this.props[key]) {
              return true;
          }
      }
      return false;
  }

  render() {
    return (
        <div>
          ...huge amount of stuff
        </div>
    );
  }
}
class Task extends React.PureComponent {
  
  /*
  shouldComponentUpdate(newProps) {
      for (const key in newProps) {
          if (newProps[key] !== this.props[key]) {
              return true;
          }
      }
      return false;
  }
  */

  render() {
    return (
        <div>
          ...huge amount of stuff
        </div>
    );
  }
}
class Grid extends React.Component {
    render() {
        const { resource, children, ids, data, currentSort } = this.props;
        return (
            <table>
                <thead>
                    <tr>
                        {React.Children.map(children, (field, index) => (
                            <DatagridHeaderCell 
                                key={index} 
                                field={field} 
                                currentSort={currentSort} 
                                updateSort={this.updateSort}
                            />
                        ))}
                    </tr>
                </thead>
                <tbody>
                    {ids.map(id => (
                        <tr key={id}>
                            {React.Children.map(children, (field, index) => (
                                <DatagridCell 
                                    record={data[id]} 
                                    key={`${id}-${index}`} 
                                    field={field} 
                                    resource={resource} />
                            ))}
                        </tr>
                    ))}
                </tbody>
            </table>
        );
    }
}
class Grid extends React.Component {
    render() {
        const { resource, children, ids, data, currentSort } = this.props;
        return (
            <table>
                <DataGridHead
                  currentSort={currentSort}
                  updateSort={this.updateSort}>
                    {children}
                </DataGridHead>
                <DataGridBody 
                  ids={ids} 
                  resource={resource} 
                  data={data}>
                    {children}
                </DataGridBody>
            </table>
        );
    }
}
class DataGridBody extends React.PureComponent {
    render() {
        const { ids, resource, data, children } = this.props;
        return (
            <tbody>
                {ids.map(id => (
                    <tr key={id}>
                        {React.Children.map(children, (field, index) => (
                            <DatagridCell 
                                record={data[id]} 
                                key={`${id}-${index}`} 
                                field={field} 
                                resource={resource} />
                        ))}
                    </tr>
                ))}
            </tbody>
        );
    }
}
class DataGridHeader extends React.PureComponent {
    render() {
        const { currentSort, updateSort, children } = this.props;
        return (
            <thead>
                <tr>
                    {React.Children.map(children, (field, index) => (
                        <DatagridHeaderCell 
                            key={index} 
                            field={field} 
                            currentSort={currentSort} 
                            updateSort={updateSort}
                        />
                    ))}
                </tr>
            </thead>
        );
    }
}

Links

  • https://marmelab.com/blog/2017/02/06/react-is-slow-react-is-fast.html#cutting-components-to-optimize-them
  • https://medium.com/@joomiguelcunha/react-performance-tips-5fa199a450b2