-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core(byte-efficiency): use log-normal distribution scoring #14977
core(byte-efficiency): use log-normal distribution scoring #14977
Conversation
ByteEfficiencyAudits
to use log-normal distribution scoring (#11883)ByteEfficiencyAudits
to use log-normal distribution scoring
ByteEfficiencyAudits
to use log-normal distribution scoringByteEfficiencyAudits
to use log-normal distribution scoring
ByteEfficiencyAudits
to use log-normal distribution scoringByteEfficiencyAudits
ByteEfficiencyAudits
ByteEfficiencyAudits
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.
Thanks for putting in a lot of work here @robatron! This looks good to me.
@brendankenny do you have any thoughts?
@robatron looks like you need to rebase and update a few unit tests for CI to pass |
Nope! Seems reasonable and the resulting curve is a nice fit @robatron! I think the only thing is how it will affect scores in practice, which we should see when the tests are updated. The curve is well behaved and similar enough to the existing one that I can't imagine anything fatal will pop up. @robatron you'll need to run |
// log-normal cumulative distribution function curve to the former method of linear interpolation | ||
// scoring between the control points {average = 300 ms, poor = 750 ms, zero = 5000 ms} using the | ||
// curve-fit tool at https://mycurvefit.com/ rounded to the nearest integer. See | ||
// https://www.desmos.com/calculator/gcexiyesdi for an interactive visualization of the curve fit. |
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.
great demo!
5068e33
to
f76e799
Compare
@brendankenny, done! Tests are all passing for me locally now ✅ |
package.json
Outdated
@@ -10,7 +10,7 @@ | |||
"smokehouse": "./cli/test/smokehouse/frontends/smokehouse-bin.js" | |||
}, | |||
"engines": { | |||
"node": ">=16.16" | |||
"node": ">=16.16 <19" |
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 noticed tests fail part way through with confusing engine errors in node v19, but work fine v18 (latest LTS). Thought it might be good to cap the node version compatibility for friendlier error messages to help avoid confusion. What do you all think?
Before 👎
[I] ➜ node --version
v19.8.0
[I] ➜ yarn test
yarn run v1.22.18
< ... about 450 lines later ... >
$ bash flow-report/test/run-flow-report-tests.sh
+++ dirname flow-report/test/run-flow-report-tests.sh
++ cd flow-report/test
++ pwd
+ SCRIPT_DIR=/Users/robmc/code/lighthouse/flow-report/test
+ LH_ROOT=/Users/robmc/code/lighthouse/flow-report/test/../..
+ ARGS=(--testMatch='{flow-report/**/*-test.ts,flow-report/**/*-test.tsx}' --require="$LH_ROOT/flow-report/test/setup/env-setup.ts")
+ cd /Users/robmc/code/lighthouse/flow-report/test/../..
+ node --loader=@esbuild-kit/esm-loader core/test/scripts/run-mocha-tests.js '--testMatch={flow-report/**/*-test.ts,flow-report/**/*-test.tsx}' --require=/Users/robmc/code/lighthouse/flow-report/test/../../flow-report/test/setup/env-setup.ts
(node:66690) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
running 12 test files
/usr/local/Cellar/node/19.8.0/bin/node[66690]: ../src/module_wrap.cc:599:MaybeLocal<v8::Promise> node::loader::ImportModuleDynamically(Local<v8::Context>, Local<v8::Data>, Local<v8::Value>, Local<v8::String>, Local<v8::FixedArray>): Assertion `(it) != (env->id_to_function_map.end())' failed.
1: 0x104bd587c node::Abort() [/usr/local/Cellar/node/19.8.0/bin/node]
2: 0x104bd5867 node::Abort() [/usr/local/Cellar/node/19.8.0/bin/node]
3: 0x104b97745 node::loader::ImportModuleDynamically(v8::Local<v8::Context>, v8::Local<v8::Data>, v8::Local<v8::Value>, v8::Local<v8::String>, v8::Local<v8::FixedArray>) [/usr/local/Cellar/node/19.8.0/bin/node]
4: 0x104e6006f v8::internal::Isolate::RunHostImportModuleDynamicallyCallback(v8::internal::MaybeHandle<v8::internal::Script>, v8::internal::Handle<v8::internal::Object>, v8::internal::MaybeHandle<v8::internal::Object>) [/usr/local/Cellar/node/19.8.0/bin/node]
5: 0x1051d3ac3 v8::internal::Runtime_DynamicImportCall(int, unsigned long*, v8::internal::Isolate*) [/usr/local/Cellar/node/19.8.0/bin/node]
6: 0x104a162b4 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit [/usr/local/Cellar/node/19.8.0/bin/node]
flow-report/test/run-flow-report-tests.sh: line 20: 66690 Abort trap: 6 node --loader=@esbuild-kit/esm-loader core/test/scripts/run-mocha-tests.js ${ARGS[*]} "$@"
error Command failed with exit code 134.
After 👍
[I] ➜ node --version
v19.8.0
[I] ➜ yarn test
yarn run v1.22.18
error [email protected]: The engine "node" is incompatible with this module. Expected version ">=16.16 <19". Got "19.8.0"
error Commands cannot run with an incompatible environment.
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 needs to be done in a breaking change. I'll add a note to address this in #14909 but we should remove it in this PR along with the .nvmrc
.
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 can repro in 19.8.0, but only in that version. @robatron looks like you were unlucky ending up on that version and not 19.8.1 which was released the next day with what looks like a fix: https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V19.md#2023-03-15-version-1981-current-targos
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.
Lol, yep, > 19.8.0 seems to work fine. @adamraine , I reverted this and removed the .nvmrc
.
… (latest LTS)" This reverts commit 29a9a48.
ByteEfficiencyAudits
Hi all!
This is my first PR to Lighthouse, and my first experience with log-normal distributions and curve-fitting, so please bear with me 😅
My intention is to address issue #11883. (Selected from the good first issue label.) Hopefully my approach isn't too far off, but in case it is, I hope this PR can still serve as a starting point for someone more familiar with these topics (or at least spark some discussion!)
ByteEfficiencyAudit
to use log-normal distribution scoringSummary
This PR updates
ByteEfficiencyAudits
to use standard log-normal distribution scoring which replaces a custom method based on a linear piecewise function. Parameters for the log-normal distribution scoring were selected by fitting its CDF curve to the original linear piecewise function using an online curve fitting tool.Details
The
ByteEfficiencyAudit.scoreForWastedMs()
function uses a non-standard scoring method defined by linear interpolation between control points -(0, 1)
,(300, 0.75)
,(750, 0.5)
, and(5000, 0)
- forming a linear piecewise function.desmos.com/dpuygs6bdh
To use log-normal distribution scoring, we need to find values for the two required parameters,
p10
andmedian
, such that the curve of the log-normal CDF fits this linear piecewise function. As suggested by @koraa in #11883, we can find these values with a function-fitting tool.In this case, I opted for an online function fitting tool, MyCurveFit. MyCurveFit accepts a list of data points and a "fit function" containing coefficients, then computes the optimal coefficient values that best align the fit function with the given data points.
Generating data points for the linear piecewise function
MyCurveFit requires a list of up to 500 data points to fit the curve of the fit function. To approximate our linear piecewise function, we can use a spreadsheet to linearly interpolate between the control points (calculating the piecewise function's value at every 10th point between 0 and 5,000.)
Note: I found that relying solely on the control points as the target led to a suboptimal fit b/c the curve-fitting process would focus on those points only, ignoring the points in between. Generating additional data points through linear interpolation as described above resulted in a better fit. Based on this, I wonder if 500 data points is enough for an accurate fit? Or are there better curve-fitting tools that would accept continuous functions as their target? 🤔
Defining a fit function from the log-normal CDF
MyCurveFit now needs a "fit function" that will be fit to the data points of the linear piecewise function. Deriving this fit function from the log-normal CDF used in
getLogNormalScore()
, we get:... which was derived like so:
Start with the original log-normal CDF code from statistics.js:
Substitute
standardizedX
:Substitute
xLogRatio
,INVERSE_ERFC_ONE_FIFTH
, andp10LogRatio
:Substitute
xRatio
, andp10Ratio
:Remove
Math.max(Number.MIN_VALUE, ...)
(b/c we assumevalue
,median
, andp10
are all > 0)[Final step] Substitute JavaScript → MyCurveFit syntax: Replace
Math.log()
→ln()
,-ln(A / B)
→ln(B / A)
,value
→x
(independent variable),median
→m, and
p10→
p` (coefficients)To validate the derived fit function, we can plot it alongside the original log-normal CDF (defined in desmos.com/o98tbeyt1t from
statistics.getLogNormalScore()
). We would expect them to produce identical curves, which they do ✅Original log-normal CDF (in blue) and the derived fit function (in red) producing identical curves (desmos.com/dpuygs6bdh)
Fitting the log-normal CDF to the linear piecewise function
Now that we've entered our data points and fit function into MyCurvefit, we can perform the curve fitting process. This will provide the best-fitting values for our coefficients,
p10
andmedian
, so that the fit function matches the data points as closely as possible.Results:
p10
=150.2663
median
=934.7609
Results of fitting the log-normal CDF to the linear piecewise function (MyCurveFit)
Updating
ByteEfficiencyAudit
to use log-normal distribution scoringWith the values for
p10
andmedian
obtained from the curve-fitting process described above, we can now updateByteEfficiencyAudit.scoreForWastedMs()
to use the standard log-normal distribution scoring method:Conclusion
In this PR, I aimed to address issue #11883 by replacing the custom linear piecewise function scoring method in
ByteEfficiencyAudits
with standard log-normal distribution scoring. I obtained the required parameters (p10
andmedian
) for the log-normal distribution by fitting its CDF curve to the original linear piecewise function using an online curve fitting tool, MyCurveFit.(Again, if my approach is off track, I hope this PR can still serve as a starting point for someone more familiar with these topics or at least spark some discussion ✨)
Thank you for your time reviewing this PR. I look forward to your feedback!