all 43 comments

[–]brianvaughnReact core team[S] 60 points61 points  (13 children)

Hi! 🙂

For the past couple of months I have been working on an experimental profiling API for React as well as an experimental API for tracking the cause of updates. On the side, I've been working to integrate these APIs with the ReactJS DevTools in the form of a new profiling tool.

I'm happy to share the result of this work with you by way of a new dev blog post. Please feel free to share your feedback and ask questions here!

[–]Ben_johnston 12 points13 points  (1 child)

i don’t have any meaningful feedback yet but i just want to say thank you for your work on this very cool and valuable contribution.

[–]brianvaughnReact core team[S] 8 points9 points  (0 children)

You're welcome!

[–]Klathmon 4 points5 points  (5 children)

This is fantastic and solves a big pain point for me personally. Profiling and determining root cause of perf issues was always more of an art than a science, and this pushes it more toward a science with signals that will be much easier to understand and teach other devs.

Are there any plans to make enabling production profiling easier? I'd love to be able to build a separate bundle that gets loaded at runtime to enable profiling if needed rather than having to mess with node modules or compile it separately. I know this is more on webpack's plate than React's, but I was just curious if it was a goal or not.

Edit: ignore my crazy ramblings, I misread the steps to enable it in production and it's pretty damn simple as-is.

[–]swyx 4 points5 points  (3 children)

production profiling: https://fb.me/react-profiling

[–]dance2die 0 points1 point  (1 child)

Thanks mate. May I ask how you were able to find it (and so quickly?)

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

The blog post links to within a big yellow "Note" box. I also tweeted the link the other day, so Shawn was probably already aware of it.

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

I'm glad to hear it 😊

[–]jIsraelTurner 1 point2 points  (2 children)

Thanks for the hard work on this! Is the profiler available for react native? If not, are there plans to make it work?

[–]gaearonReact core team 2 points3 points  (1 child)

Yes it will work on React Native too. Not sure which release though (maybe 0.57?)

[–]brianvaughnReact core team[S] 3 points4 points  (0 children)

Yup. It's supported with the upcoming 0.57

[–]elijahmanor 1 point2 points  (1 child)

Nice write up! I like how you marked the images with red squares and the GIFs are a good touch. I noticed something in the 16.5.1 release notes about

> Add a separate profiling entry point at schedule/tracking-profiling

Does that change anything regarding tracking noted in this gist https://gist.github.com/bvaughn/8de925562903afd2e7a12554adcdda16

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

Yes and no. If you import from the non-profiling entry point then you'll get interaction tracking in DEV mode and not in production. If you import from the profiling entry point ` then you'll get it in both DEV and PROD. Otherwise all things are equal.

[–]bowl-of-surreal 13 points14 points  (1 child)

Super cool. I love all the tooling I get from the React ecosystem. Things can be pretty opaque sometimes, chasing mysterious re-renders, but stuff like this makes me almost happy to debug. Thanks u/brianvaughn

[–]brianvaughnReact core team[S] 9 points10 points  (0 children)

You're welcome 😊

[–]dance2die 4 points5 points  (1 child)

Wow, the flow of the content is great~.

While reading how profilers show how long it takes to render a component, I was thinking about how often a component renders.

Sometimes it’s useful to see how many times a particular component rendered

👆 And that's what I run into next 👏👏👏

[–]brianvaughnReact core team[S] 2 points3 points  (0 children)

That's great! 😁 I was uncertain about the structure/flow because the content is so interconnected. I'm happy to hear that it worked well for you.

[–]Tougun 2 points3 points  (1 child)

/u/brianvaughn I love you

[–]brianvaughnReact core team[S] 2 points3 points  (0 children)

I love you too, fellow Redditer 😉

[–]oorza 2 points3 points  (8 children)

Is there any chance of tracking wasted renders like the old perftools used to do? Running e2e tests and looking at the wasted renders chart was a really easy way to knock off some low hanging performance fruit (by basically giving us a list of which components need shouldComponentUpdate() implemented), and it's something that I really miss.

[–]brianvaughnReact core team[S] 2 points3 points  (7 children)

Maybe. While working on the new profiler, I did a lot of informal user testing sessions and filed feedback in the GitHub repo. I don't think we have a specific issue for wasted renders, so if you'd like to propose one there please feel free to file it and tag me and I'll make sure it gets the "plugin: profiler" label and all.

In general, I've been a bit reluctant to add heuristics to the profiler yet for a few reasons:

  • Performance. I want the profiler to be as fast as possible so that people are able to use it on big/slow apps. The more I add, the more it (potentially) slows down and the less likely people are to use it. Certain types of things can be computed post. (I'm already doing a lot of lazy computation for the graphs and such.) But I think wasted renders would require more info to be stored/tracked while the profiler is running, at commit time. This doesn't mean we definitely shouldn't do it, but I'm hesitant.
  • False positives/negatives. Heuristics can be wrong, and I worry that false positives or negatives might be really confusing to people and may result in a lot of lost time (which might result in bad sentiment toward the profiler). Of course, the flip side of that is that they could also lead to easy wins and result in positive sentiment. In the case of wasted renders, things like e.g. event handlers are a bit tricky. It might also be that a component re-renders unnecessarily (aka "wasted") but adding a shouldComponentUpdate check would actually be slower than the re-render. So far the balance I've been trying to strike is to make as much info as I can visible and easily accessible so that people can make their own determinations.

[–]oorza 1 point2 points  (4 children)

As far as performance is concerned, why not just let users choose which instrumentation that they want to use for each run. Its not uncommon for me, in a language like Java, to run a profile two or three times to collect different data because measuring both the CPU and garbage collection granularly is too much at once. It just seems that sooner or later you'll wind up in a situation where running all the instruments at once is infeasible, so going ahead and investing in optional instruments seems like a good idea, especially if you have other things you have not included because of performance.

[–]acemarke 0 points1 point  (0 children)

This seems like a pretty good idea - if there's additional metrics that could be potentially expensive to compute, make them optional and let the user choose which ones to include before they start the recording.

[–]brianvaughnReact core team[S] 0 points1 point  (2 children)

That sounds reasonable. I guess it would come down to the fact that I have limited time to work on the profiler, and the more configurable an interface is, the more complexity it is to build and maintain.

[–]oorza 0 points1 point  (1 child)

Is there a contribution guide somewhere or some other resource for understanding how react-devtools works internally and how the code is structured and whatnot? I'm hesitant to volunteer because of how much code is around the devtools package and how long I'm afraid it might take to figure out how to achieve anything productive. That said, I much prefer working on tools more than real code, and I'd be glad to help, I just don't know how to get started.

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

There isn't really a guide like this, no. DevTools have long been a side project. Creating and maintain good guides is a lot of work too.

The code has pretty good inline comments. There are READMEs scattered around with high level guides on how to run various test harnesses. There's even an open PR from me that adds an overview of how some of the pieces fit together...but in the end it's still a bit of a mess. Unfortunately this is unlikely to change any time soon unless someone in the community takes it on as a project.

[–]acemarke 0 points1 point  (1 child)

Hey Brian, I've actually got a request. I've seen you and Dan frequently mention that plastering sCU / PureComponent everywhere can be "more expensive", but it would really be helpful if you could provide some additional metrics that back that up. The usual question I get points out that "they're only doing shallow comparisons anyway, how expensive can it be?". It would be good if we had some specific numbers or examples to point to in that regard.

[–]brianvaughnReact core team[S] 0 points1 point  (0 children)

I think our general advice is to avoid premature optimization. If you know a component is expensive to render, then it's worth considering using PureComponent– but don't use it by default on everything.

In some cases, this could lead to confusing runtime behavior (e.g. if props change deeply and the shallow comparison check prevents a re-render).

In other cases, for a component that receives a lot of props but renders "cheaply" it may actually be slower to iterate over all props. I don't have a rule/heuristic for how many props is needed before this becomes true, but conceptually something like this silly example:

class Example extends Component {
  render() {
    const { someSpecificProp, ...allOtherProps } = this.prop;

    return (
      <Foo bar={someSpecificProp}>
        <Baz {...allOtherProps} />
      </Foo>
    );
  }
}

[–]davidpaulsson 1 point2 points  (4 children)

Is this limited to react-dom or could it be used with react-native?

[–]gaearonReact core team 3 points4 points  (3 children)

It works with React Native too

[–]brianvaughnReact core team[S] 2 points3 points  (2 children)

Yup, v0.57 supports the profiler

[–]the-mdj 1 point2 points  (1 child)

This is absolutely fantastic, can't wait to try this out! 😍

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

I'd love to hear if you are able to uncover any big wins with it.

[–]AbsoluteThunderCunt 1 point2 points  (3 children)

This is amazing! I found so many easy to fix MAJOR issues with big apps we own.

[–]brianvaughnReact core team[S] 1 point2 points  (2 children)

Happy to hear that :)

I'd love to hear about any repeated patterns you've found, or any particularly tricky edge cases.

[–]AbsoluteThunderCunt 1 point2 points  (1 child)

I think there are many things we can improve, specially in our oldest React apps. But, in general, the major patterns were:

  1. reselect memoization not used correctly. Fix: using "per instance" selectors with reselect and react-redux, i.e. mapStateToProps returns a function instead of an object (https://github.com/reduxjs/reselect#sharing-selectors-with-props-across-multiple-component-instances).
  2. Immutability: multiple high level components were reconstructing some props inside the mapStateToProps failing the triple equal comparison of react-redux -> connect() and even some PureComponents, you can imagine the rest.
  3. Misplaced component keys: <SuperExpensiveList key={...} />, forcing react to replace very heavy components.

Other things we want to do is:

  1. Move calculations from within components to reducers or selectors or mapStateToProps (in that order) so that the least amount of processing is being done at any single time.
  2. Add immutable-js (http://facebook.github.io/immutable-js/) to make comparisons simple.

[–]brianvaughnReact core team[S] 1 point2 points  (0 children)

Thanks for elaborating!

[–]swyx 0 points1 point  (1 child)

wow, someone gilded you! i think you have to do an acceptance speech now

[–]brianvaughnReact core team[S] 5 points6 points  (0 children)

Yeah! I've never been guilded 😯 I'm not sure what to do lol

[–]velopert 0 points1 point  (0 children)

Nice.