Skip to content
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

feat: add --fast mode #49

Merged
merged 6 commits into from
May 11, 2017
Merged

feat: add --fast mode #49

merged 6 commits into from
May 11, 2017

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented May 8, 2017

This PR changes the progress calculation to optionally skip frames that are between ones that have similar progress toward the target. When enabled, it speeds up processing by about 40% and has minimal impact when not enabled via the shortcut. This also aligns perceptual progress and visual progress by sharing the same computation code and fixes #48 by virtue of the fact that we can't wait for the global min when we only parse some of the JPEGs.

Timings

( for i in $(seq 10); do; speedline cnn.json --pretty; done; )  55.15s user 4.63s system 98% cpu 1:00.44 total

( for i in $(seq 10); do; speedline cnn.json --pretty --fast; done; )  34.28s user 3.32s system 99% cpu 37.737 total

1 - (34/55) = ~38%

Open Questions

  • Expose the direct threshold value as the option or keep as binary? If so, name? similarProgressThreshold?

@pahammad
Copy link

pahammad commented May 8, 2017

Skipping frames can artificially make the visual jitter (jankiness) go away from the calculations. Please make sure that layout instability effects that perceptual speed index captures aren't sacrificed due to this frame-skipping (technically, this is temporal downsampling).

@patrickhulce
Copy link
Collaborator Author

patrickhulce commented May 8, 2017

@pahammad by its nature, the impact on the indexes cannot be avoided as you point out. However, in my experience there are a number of issues with using these metrics too as a signal of layout stability in the first place and frequently I've seen jitter artificially be rewarded by speed index rather than punished. Would a warning and tunable threshold address your concerns or are you against including the option at all with these drawbacks?

@pahammad
Copy link

pahammad commented May 9, 2017

I am OK with including the frame-skipping as an option if it comes with appropriate warning when the option is exercised. I am very curious about your sentence: "frequently I've seen jitter artificially be rewarded by speed index rather than punished". Are you referring to the classical (histogram-based) speed index, or are you referring to SSIM based perceptual speed index (PSI) ? If you are referring to PSI in that sentence (where it rewards jitter instead of punishing it), I would love to see an example or two - this is opposite to what I've seen.

@patrickhulce
Copy link
Collaborator Author

@pahammad disclaimer text added and I've filed #50 to discuss layout stability tracking 👍

Copy link
Owner

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

thanks for doing this. will be a big help.

@@ -136,6 +136,15 @@ test('speed indexes calculated for realistic trace', async t => {
t.is(Math.floor(indexes.perceptualSpeedIndex), 578);
});

test('speed indexes calculated for realistic trace with --fast', async t => {
Copy link
Owner

Choose a reason for hiding this comment

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

let's also assert that we didn't call getPerceptualProgress, etc on every frame, since that's the essence of this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

frames,
0,
frames.length - 1,
opts && opts.fast ? 5 : 0,
Copy link
Owner

Choose a reason for hiding this comment

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

set 5 as a constant above?

reading this line it's hard to understand what "5" as a "threshold" even means.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yup will do

0,
frames.length - 1,
opts && opts.fast ? 5 : 0,
frame => {
Copy link
Owner

Choose a reason for hiding this comment

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

extract this and the next fn into named variables

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

0,
frames.length - 1,
opts && opts.fast ? 5 : 0,
frame => frame.getProgress() || calculateFrameProgress(frame, initial, target),
Copy link
Owner

Choose a reason for hiding this comment

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

maybe not enough coffee, but ... brain hurts.

this is an expression of fn1 || fn2 and will be passed in as the getProgress parameter.
when called, the fn1 will evaluate and then return and if that result is falsy then the fn2 will then run?

is there another way to model this?


if (Math.abs(lowerProgress - upperProgress) < threshold) {
for (let i = lowerBound; i < upperBound; i++) {
setProgress(frames[i], lowerProgress);
Copy link
Owner

Choose a reason for hiding this comment

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

should we also set a property on the frame indicating this value was interpolated? just an idea.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah good plan, make the assertion tests easy too

const lowerProgress = getProgress(lowerFrame);
const upperProgress = getProgress(upperFrame);

setProgress(lowerFrame, lowerProgress);
Copy link
Owner

Choose a reason for hiding this comment

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

should we just put the setProgress call inside of our custom getProgress fn?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that seems to breakdown the perf win of differentiating between setting the progress and parsing the jpeg to get progress, no?

Copy link
Owner

Choose a reason for hiding this comment

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

well i meant we cache the result value and only do the work if necessary.
but admittedly there isn't a nice way to do this with the frame object API.

aiight nevermind.

setProgress(lowerFrame, lowerProgress);
setProgress(upperFrame, upperProgress);

if (Math.abs(lowerProgress - upperProgress) < threshold) {
Copy link
Owner

Choose a reason for hiding this comment

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

as discussed earlier i'm thinking we might want to threshold on time as well.

const FastMode_FrameProgressDelta = 5;  // units: percent
const FastMode_TimeDelta = 200;   // units: ms

wdyt?
this way we can handle a 5% change over 5 seconds (probably worth inspecting) vs a 5% change over 100ms (not worth it)

Copy link
Collaborator Author

@patrickhulce patrickhulce May 9, 2017

Choose a reason for hiding this comment

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

yeah I like it, but it'll likely erase a lot of the perf win if it's just straight up &&. In the CNN case, the same exact image is used across ~10s of the trace which accounts for ~30 frames. Thoughts on combining progress and time?

wdyt of using that fun exponentiation window size function from TTFI?

allowable percent change = f(time elapsed in seconds) = 4 * e^(-.69 * t) + 1
f(0) = 4 + 1 = 5% allowed change when elapsed time is 0s
f(1) = 2 + 1 = 3% allowed change when elapsed time is 1s
f(∞) = 0 + 1 = 1% allowed change when elapsed time is very large

Copy link
Owner

Choose a reason for hiding this comment

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

yeah i like that approach.

@@ -2,6 +2,31 @@

const imageSSIM = require('image-ssim');

/* BEGIN FAST MODE CONSTANTS - See function doc for explanation */
const FAST_MODE_ALLOWABLE_CHANGE_MAX = 5;
Copy link
Owner

Choose a reason for hiding this comment

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

speedline doesnt neccessarily follow the google js style guide.

i'd love to take that opportunity to not caps_lock our constants :)

You good with something like... fastModeAllowableChangeMax ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

haha sure

*
* f(t) = FAST_MODE_MULTIPLIER * e^(FAST_MODE_EXPONENTIATION_COEFFICIENT * t) + FAST_MODE_CONSTANT
*/
function calculateFastModeAllowableChange(elapsedTime) {
Copy link
Owner

Choose a reason for hiding this comment

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

can we add a few tests for calculateFastModeAllowableChange to just to confirm our landmark values give the right results

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

cli.js Outdated
console.warn('WARNING: using --fast may result in different metrics due to skipped frames');
}

speedIndex(filePath, {fast: cli.flags.fast}).then(function (res) {
Copy link
Owner

Choose a reason for hiding this comment

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

let's change this opt property name from fast to fastMode

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

t.is(Math.floor(indexes.speedIndex), 4360);
t.is(Math.floor(indexes.perceptualSpeedIndex), 4360);

// Ensure we skipped computation of most of the 60 fps frames
Copy link
Owner

Choose a reason for hiding this comment

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

loooooooove it

@paulirish
Copy link
Owner

changes lgtm but 2 test failures we need to work out.

@patrickhulce
Copy link
Collaborator Author

changes lgtm but 2 test failures we need to work out.

done

@paulirish paulirish merged commit 4998ef6 into paulirish:master May 11, 2017
@paulirish
Copy link
Owner

[email protected] published with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

White frame perceptual progress can be significantly greater than 0
3 participants