-
Notifications
You must be signed in to change notification settings - Fork 143
Conversation
There was a problem hiding this 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.
lib/Component.lua
Outdated
|
||
local Component = {} | ||
|
||
-- Locally cache tick so we can minimize impact of calling it for instrumentation | ||
local l_tick = tick |
There was a problem hiding this comment.
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.
There was a problem hiding this 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!
lib/Instrumentation.lua
Outdated
|
||
local Instrumentation = {} | ||
|
||
local ComponentStats = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: camelCase variable name
lib/Instrumentation.lua
Outdated
|
||
local function valuesByStat(t, f) | ||
local a = {} | ||
for _, obj in pairs(t) do table.insert(a, obj) end |
There was a problem hiding this comment.
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?
lib/Instrumentation.lua
Outdated
-- Percent of time shouldUpdate returns true | ||
-- Percent of total shouldUpdate time by component | ||
|
||
local function valuesByStat(t, f) |
There was a problem hiding this comment.
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?
lib/Instrumentation.lua
Outdated
local trackRenders = GlobalConfig.getValue("renderInstrumentation") | ||
|
||
if not trackUpdates and not trackRenders then | ||
print("No stats are being tracked! Enable with Roact.setConfig({...})") |
There was a problem hiding this comment.
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
!
lib/Instrumentation.lua
Outdated
end | ||
print(statsRow) | ||
end | ||
if trackUpdates then |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
lib/Instrumentation.lua
Outdated
local i = 0 -- iterator variable | ||
local iter = function () -- iterator function | ||
i = i + 1 | ||
if a[i] == nil then |
There was a problem hiding this comment.
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]
.
lib/Instrumentation.lua
Outdated
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 |
There was a problem hiding this comment.
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
lib/Instrumentation.lua
Outdated
local entry = ComponentStats[name] | ||
if not entry then | ||
-- Use shortened name for convenience; | ||
local shortName = name:gsub("Connection","Conn") |
There was a problem hiding this comment.
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).
@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. |
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 |
lib/init.lua
Outdated
apply(Roact, { | ||
getCollectedStats = Instrumentation.getCollectedStats, | ||
clearCollectedStats = Instrumentation.clearCollectedStats, | ||
}) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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.
fbfc457
to
7d17213
Compare
@LPGhatguy Any other changes you'd like to see before merging this? |
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, |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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.
f032e73
to
77aaa43
Compare
… methods directly in init.lua; add a bit of detail to some comments
…ly and made slight changes for clarity.
edd68a2
to
cd3e885
Compare
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. |
Retrying my approach to tracking performance. This will gather stats about
render
calls andshouldUpdate
calls, and should be able to provide info that would help indicate when to make aComponent
aPureComponent
or vice versa, when to write a customshouldUpdate
, or when to try to cut down on the complexity ofrender
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).