This repository has been archived by the owner on Dec 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
255 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
local Instrumentation = {} | ||
|
||
local componentStats = {} | ||
-- Tracks a number of stats, including: | ||
-- Recorded stats: | ||
-- Render count by component | ||
-- Update request count by component | ||
-- Actual update count by component | ||
-- shouldUpdate returned true count by component | ||
-- Time taken to run shouldUpdate | ||
-- Time taken to render by component | ||
-- Derivable stats (for profiling manually or with a future tool): | ||
-- Average render time by component | ||
-- Percent of total render time by component | ||
-- Percent of time shouldUpdate returns true | ||
-- Average shouldUpdate time by component | ||
-- Percent of total shouldUpdate time by component | ||
|
||
--[[ | ||
Determines name of component from the given instance handle and returns a | ||
stat object from the componentStats table, generating a new one if needed | ||
]] | ||
local function getStatEntry(handle) | ||
local name | ||
if handle and handle._element and handle._element.component then | ||
name = tostring(handle._element.component) | ||
else | ||
warn("Component name not valid for " .. tostring(handle._key)) | ||
return nil | ||
end | ||
local entry = componentStats[name] | ||
if not entry then | ||
entry = { | ||
-- update requests | ||
updateReqCount = 0, | ||
-- actual updates | ||
didUpdateCount = 0, | ||
-- time spent in shouldUpdate | ||
shouldUpdateTime = 0, | ||
-- number of renders | ||
renderCount = 0, | ||
-- total render time spent | ||
renderTime = 0, | ||
} | ||
componentStats[name] = entry | ||
end | ||
|
||
return entry | ||
end | ||
|
||
--[[ | ||
Logs the time taken and resulting value of a Component's shouldUpdate function | ||
]] | ||
function Instrumentation.logShouldUpdate(handle, updateNeeded, shouldUpdateTime) | ||
-- Grab or create associated entry in stats table | ||
local statEntry = getStatEntry(handle) | ||
if statEntry then | ||
-- Increment the total number of times update was invoked | ||
statEntry.updateReqCount = statEntry.updateReqCount + 1 | ||
|
||
-- Increment (when applicable) total number of times shouldUpdate returned true | ||
statEntry.didUpdateCount = statEntry.didUpdateCount + (updateNeeded and 1 or 0) | ||
|
||
-- Add time spent checking if an update is needed (in millis) to total time | ||
statEntry.shouldUpdateTime = statEntry.shouldUpdateTime + shouldUpdateTime * 1000 | ||
end | ||
end | ||
|
||
--[[ | ||
Logs the time taken value of a Component's render function | ||
]] | ||
function Instrumentation.logRenderTime(handle, renderTime) | ||
-- Grab or create associated entry in stats table | ||
local statEntry = getStatEntry(handle) | ||
if statEntry then | ||
-- Increment total render count | ||
statEntry.renderCount = statEntry.renderCount + 1 | ||
|
||
-- Add render time (in millis) to total rendering time | ||
statEntry.renderTime = statEntry.renderTime + renderTime * 1000 | ||
end | ||
end | ||
|
||
--[[ | ||
Clears all the stats collected thus far. Useful for testing and for profiling in the future | ||
]] | ||
function Instrumentation.clearCollectedStats() | ||
componentStats = {} | ||
end | ||
|
||
--[[ | ||
Returns all the stats collected thus far. Useful for testing and for profiling in the future | ||
]] | ||
function Instrumentation.getCollectedStats() | ||
return componentStats | ||
end | ||
|
||
return Instrumentation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
return function() | ||
local Component = require(script.Parent.PureComponent) | ||
local Core = require(script.Parent.Core) | ||
local GlobalConfig = require(script.Parent.GlobalConfig) | ||
local Instrumentation = require(script.Parent.Instrumentation) | ||
local Reconciler = require(script.Parent.Reconciler) | ||
|
||
it("should count and time renders when enabled", function() | ||
GlobalConfig.set({ | ||
["componentInstrumentation"] = true, | ||
}) | ||
local triggerUpdate | ||
|
||
local TestComponent = Component:extend("TestComponent") | ||
function TestComponent:init() | ||
self.state = { | ||
value = 0 | ||
} | ||
end | ||
|
||
function TestComponent:render() | ||
return nil | ||
end | ||
|
||
function TestComponent:didMount() | ||
triggerUpdate = function() | ||
self:setState({ | ||
value = self.state.value + 1 | ||
}) | ||
end | ||
end | ||
|
||
local instance = Reconciler.reify(Core.createElement(TestComponent)) | ||
|
||
local stats = Instrumentation.getCollectedStats() | ||
expect(stats.TestComponent).to.be.ok() | ||
expect(stats.TestComponent.renderCount).to.equal(1) | ||
expect(stats.TestComponent.renderTime).never.to.equal(0) | ||
|
||
triggerUpdate() | ||
expect(stats.TestComponent.renderCount).to.equal(2) | ||
|
||
Reconciler.teardown(instance) | ||
Instrumentation.clearCollectedStats() | ||
GlobalConfig.reset() | ||
end) | ||
|
||
it("should count and time shouldUpdate calls when enabled", function() | ||
GlobalConfig.set({ | ||
["componentInstrumentation"] = true, | ||
}) | ||
local triggerUpdate | ||
local willDoUpdate = false | ||
|
||
local TestComponent = Component:extend("TestComponent") | ||
|
||
function TestComponent:init() | ||
self.state = { | ||
value = 0, | ||
} | ||
end | ||
|
||
function TestComponent:shouldUpdate() | ||
return willDoUpdate | ||
end | ||
|
||
function TestComponent:didMount() | ||
triggerUpdate = function() | ||
self:setState({ | ||
value = self.state.value + 1, | ||
}) | ||
end | ||
end | ||
|
||
function TestComponent:render() end | ||
|
||
local instance = Reconciler.reify(Core.createElement(TestComponent)) | ||
|
||
local stats = Instrumentation.getCollectedStats() | ||
|
||
willDoUpdate = true | ||
triggerUpdate() | ||
|
||
expect(stats.TestComponent).to.be.ok() | ||
expect(stats.TestComponent.updateReqCount).to.equal(1) | ||
expect(stats.TestComponent.didUpdateCount).to.equal(1) | ||
|
||
willDoUpdate = false | ||
triggerUpdate() | ||
|
||
expect(stats.TestComponent.updateReqCount).to.equal(2) | ||
expect(stats.TestComponent.didUpdateCount).to.equal(1) | ||
expect(stats.TestComponent.shouldUpdateTime).never.to.equal(0) | ||
|
||
Reconciler.teardown(instance) | ||
Instrumentation.clearCollectedStats() | ||
GlobalConfig.reset() | ||
end) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters