Skip to content

Performance tips

@greweb edited this page Jul 7, 2022 · 1 revision

General mindset and tips

  • Profiling and performance is a budget to acknowledge for developers as part of developing a feature as write tests (and docs) will be more and more.
  • Be pragmatic: focus on quick wins. We want to maximize the (perf gain / dev cost) when optimising. Don't make code cryptic and unmaintainable for 5% gain. https://github.com/mikolalysenko/box-intersect/blob/master/lib/partition.js
  • It is often easy to break performance but harder to detect regression.
  • Prefer smaller chunk of code executed rather than a big one to avoid frame drop.
  • Time measurements is relative: it's worse in dev mode and it's computer dependent, prefer thinking in relative percentage ("I've improved this by making it 3x faster") – even relative is not 100% accurate of prod conditions.
  • Profiling in context of real conditions (computers) and in production build is an interesting metrics too. Maybe more in scope of QA. we should test in shitty computers where CPU is slow, FS is slow,.. to get better and more precise metrics (we can do that on a less frequent basis)
  • It's easy to get biased by a local minima. Always consider a wide variety of cases. There are plenty of edge cases in our fields (e.g. mining accounts vs usual accounts with lot of txs)
  • Our goal should be to never drop a single frame in renderer thread in production mode. (we have main and internal threads for consuming work). In a more practical order of magnitude, nothing should take longer than 100ms under dev mode. (Assuming it's 6x worse)

Profiling: don't trust, verify!

  • renderer: Best tool is Chrome Devtools / Performance tab. Simply record there and expand the relevant section (like User section is very useful for React, can precisely know what rerenders). there is also React profiler but I believe it's secondary.
  • main: ELECTRON_ARGS=--inspect-brk and go to chrome://inspect . brk mode waits for you to start inspecting.
  • internal: similar to main but with LEDGER_INTERNAL_ARGS=--inspect, on chrome://inspect, have to manually add the port displayed in the console. interesting to measure general command performance. To work with device, we'll have to use with a proxy –_–
  • Make sure to import your project in filesystem for proper sourcemap visibility and coverage.
  • CLI: we can use --inspect as well (node --inspect cli/lib/cli.js ...). Another and maybe more interesting way is to use --cpu-prof instead of inspect. It will generate cpuprofile you can import. (note: do you have a solution to make the CLI itself accept these param to forward to node args?)

Profiling the boot time

  • If possible, defer require execution to save boot time
  • Look at potential code tree that doesn't need to even be loaded. Something you can modularize more to avoid unexpected load (e.g. main thread was loading hw logic

Profiling the updates

  • look at chart timeline level
  • look at other modes to identify CPU bottlenecks

React related performance. It's a long story...

https://www.debugbear.com/blog/measuring-react-app-performance

  • React is performant. Our usage of it / components maybe isn't.
  • We should batch redux updates when necessary. Definitely for sync cases
  • We should minimize the surface of stores update to minimize rerendering
  • React concurrent mode? Can we try it on specific parts like accounts list?
  • Nesting of a component have some mounting cost. And also makes profiling more annoying (big stack)
  • Use pure component and memo but not too much! There are counter examples

Limitations

  • node-hid does not work in Profiler context https://github.com/node-hid/node-hid/issues/361
  • JavaScript Profiler is more limited than Performance and does not allow User Timing API which makes us blind on things like command context or libcore calls effectively done.
  • I would dream to be able to see all threads in Performance.

Long Term Ideas

  • Long term ideas: We need to plan to move libcore in dedicated thread. An idea would be to start using the new libcore ipc way from renderer side same as we should proxy a hid transport from renderer: all is instrumented from renderer but is async and actual sync code to run in processes (that way we minimize payloads, accounts conversion is today a bottleneck). Question : does the latency created by ipc worth the cost if we have lot of communication? What's the best way to communicate from renderer to a thread where libcore runs?
  • PoC to be done using a (WebWorker?) from renderer and see if we can boot libcore there. same to study if we can boot node-hid.
Clone this wiki locally