Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Component benchmarks #65

Merged
merged 8 commits into from
Apr 23, 2018
Merged

Conversation

ZoteTheMighty
Copy link
Contributor

Retrying my approach to tracking performance. This will gather stats about render calls and shouldUpdate calls, and should be able to provide info that would help indicate when to make a Component a PureComponent or vice versa, when to write a custom shouldUpdate, or when to try to cut down on the complexity of render methods.

I didn't find a particularly clean way to inject the instrumentation into the Component code without muddling it a bit, so I'd love suggestions for integrating it more gracefully (I left TODOs to this effect).

@coveralls
Copy link

coveralls commented Apr 12, 2018

Coverage Status

Coverage increased (+0.7%) to 89.371% when pulling cd3e885 on ZoteTheMighty:component-benchmarks into bda5a4e on Roblox:master.

Copy link
Contributor

@LPGhatguy LPGhatguy left a comment

Choose a reason for hiding this comment

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

Should we instrument the reconciler as well so that we can capture functional or primitive components?

We still don't have a way to give a name to a functional component, but there's a tracking issue for it.


local Component = {}

-- Locally cache tick so we can minimize impact of calling it for instrumentation
local l_tick = tick
Copy link
Contributor

Choose a reason for hiding this comment

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

We can just call our local version tick, since Lua lets you do that.

Copy link
Contributor

@AmaranthineCodices AmaranthineCodices left a comment

Choose a reason for hiding this comment

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

This looks good, and should be really useful! I'm slightly concerned about printing tables to the output, given that:

  • The output window's size can vary - mine is fairly small, for example
  • The output window, by default, does not use a fixed-width font, so columns based on character counts aren't going to line up by default

Maybe it'd be useful to have an additional way to get the raw data being printed, so it can be visualized in a separate UI that can have a more robust table view?

EDIT: Oops! I missed your changes; disregard this review!


local Instrumentation = {}

local ComponentStats = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: camelCase variable name


local function valuesByStat(t, f)
local a = {}
for _, obj in pairs(t) do table.insert(a, obj) end
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we break this out of a single line?

-- Percent of time shouldUpdate returns true
-- Percent of total shouldUpdate time by component

local function valuesByStat(t, f)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any particular reason this is returning an iterator instead of an array? Also, can we have more descriptive variable names in this function?

local trackRenders = GlobalConfig.getValue("renderInstrumentation")

if not trackUpdates and not trackRenders then
print("No stats are being tracked! Enable with Roact.setConfig({...})")
Copy link
Contributor

Choose a reason for hiding this comment

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

Roact.setGlobalConfig, not Roact.setConfig!

end
print(statsRow)
end
if trackUpdates then
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these stats terribly useful, given that they aren't attributed to specific components?

lib/init.lua Outdated
@@ -39,6 +40,7 @@ apply(Roact, {
PureComponent = PureComponent,
Event = Event,
Change = Change,
Instrumentation = Instrumentation,
Copy link
Contributor

@AmaranthineCodices AmaranthineCodices Apr 12, 2018

Choose a reason for hiding this comment

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

We can probably only expose getCollectedStats and clearCollectedStats here instead of exposing the entire module.

local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then
Copy link
Contributor

Choose a reason for hiding this comment

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

This entire if block can be simplified to return a[i].

for _, obj in pairs(t) do table.insert(a, obj) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: no spaces between function and parentheses

local entry = ComponentStats[name]
if not entry then
-- Use shortened name for convenience;
local shortName = name:gsub("Connection","Conn")
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not shorten names like this - finding poorly-performing components could be tricky if the name gets squashed. If we absolutely must shorten names, we could try trimming the string after it passes a specific number of characters (maybe trim from the end, so ReallyLongName gets trimmed to something like ...ongName? Not sure about the exact semantics).

@ZoteTheMighty
Copy link
Contributor Author

@AmaranthineCodices I actually removed printStats in the final commit for the reasons you mentioned. For now I don't have a solid way to output results that works for everyone's workflow. The goal would be to eventually have a Roblox Studio plugin that's capable of consuming and nicely displaying these stats.

Additionally, having a giant unwieldy print function floating around in the main codebase feels weird (partly why I scrapped my previous PR). The fact that Roblox Studio's default font for the Output window is not mono-spaced is a whole different can of worms...

Anyway, the printStats function should actually be excluded from the final PR. I can squash commits together if that'd make the history cleaner.

@AmaranthineCodices
Copy link
Contributor

No, it's okay! I started reviewing before you pushed the second commit, and didn't see it before I submitted the review. Don't worry about squashing commits; @LPGhatguy will do that while merging. This looks good as of now, barring lpg's note about tick.

lib/init.lua Outdated
apply(Roact, {
getCollectedStats = Instrumentation.getCollectedStats,
clearCollectedStats = Instrumentation.clearCollectedStats,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably shouldn't put specific benchmarking functions on the top-level, at least not yet.

Copy link
Contributor Author

@ZoteTheMighty ZoteTheMighty Apr 17, 2018

Choose a reason for hiding this comment

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

That's fair. I've removed it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be okay to expose them as a table, something like Roact.Instrumentation.getCollectedStats?

Copy link
Contributor Author

@ZoteTheMighty ZoteTheMighty Apr 18, 2018

Choose a reason for hiding this comment

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

Heh, sounds closer to my original implementation. @LPGhatguy It would be good to expose them in some way so that there's some utility to these changes, especially for the sake of the other tooling I'm working on. Let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Putting them at Roact.Instrumentation.getCollectedStats would be fine.

The original version exposed them as Roact.getCollectedStats, that was the problematic variant!

Copy link
Contributor Author

@ZoteTheMighty ZoteTheMighty Apr 18, 2018

Choose a reason for hiding this comment

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

The original version (a few commits ago) actually had the whole Instrumentation object exposed, but it makes more sense to expose only the needed methods and still do it indirectly.

@ZoteTheMighty ZoteTheMighty force-pushed the component-benchmarks branch 2 times, most recently from fbfc457 to 7d17213 Compare April 18, 2018 00:17
@ZoteTheMighty
Copy link
Contributor Author

@LPGhatguy Any other changes you'd like to see before merging this?

@LPGhatguy
Copy link
Contributor

I'll pull this branch down and work with it locally a bit, and potentially have feedback!

lib/Config.lua Outdated
-- Enables instrumentation of shouldUpdate
["shouldUpdateInstrumentation"] = false,
-- Enables instrumentation of rendering
["renderInstrumentation"] = false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it's valuable to toggle instrumentation like this individually?

When working with a profiling debugger, I imagine we would either want all of the information logged, or none of it!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree wholeheartedly, especially now that I'm faced with the idea of having my plugin behave accordingly if something is disabled. I'll make that change.

@LPGhatguy
Copy link
Contributor

Okay, I'm moving forward with the merge now because this PR only minor changes to the actual reconciler code, and doesn't affect the public API (since it's marked UNSTABLE). That should give us flexibility to modify and improve the stats that we report going forward without worrying about developers depending on this API.

@LPGhatguy LPGhatguy merged commit cee83c6 into Roblox:master Apr 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants