Subject: per stream (initial) flow window size

From: Ilya Grigorik <[email-masked]>
Date: Tue, 8 Oct 2013 16:14:40 -0700

SETTINGS allows us to set a default starting window for all streams +
update this default later. However, I think there are compelling cases
where it would be beneficial to allow different streams to start with a
different initial window size (similar to optional priority in headers
frame). Given that this would effectively be a new feature... let me try to
motivate why the extra complexity is worth it based on some of the recent
work I've been involved with:

We want to make browser pre{fetch,rendering} smarter [1,2]. Meaning, we
want to be able to discover critical page resources as early as possible
and kick off fetches for those assets also. However, since some of these
assets are large (and there can be many of them), we need to control how
much data we fetch (e.g. if we are too aggressive we may slow down the host
page, and/or incur high overhead for the user).

A concrete example of this is the work we've been experimenting with in
PageSpeed [3], where we are aggressively inlining CSS/JS and even doing
"page splits": the rewriter effectively creates two pages, the first is the
"prefetch friendly" version which fits in < 15KB (one RTT), and second is
the diff that fills in the rest once the navigation is triggered via an XHR
-- if that sounds crazy, then that's not too far from the truth. That said,
we have a working prototype, and it's showing impressive results.. For
example, we can take a mobile wikipedia page, which currently takes 9s
(!!!) to first render (on a 3G profile), down to 1.6s [3] by using the
combination of prefetch + page split strategies.

What does flow control have to do this with all this? When issuing a
prefetch, we would like to be able to open a request with a lower initial
window (e.g. 15KB), and fetch just the head of the document - in most
cases, this will allow us to discover the critical resources, initiate
requests for them, etc. Then, if the navigation is triggered, we would just
increment the window and "resume" the stream to get the rest of the page...
This eliminates the need for server rewriting / page splitting, which has a
lot of gnarly edge conditions. Handling this at the transport layer would
make it much, much simpler and more powerful.

(Yes, we could lower the initial window for all streams, but that's counter
productive in majority of cases... we'd just end up sending a lot more
WINDOW_UPDATE frames for non-prefetch request.)

Further, the ability to set a custom window size also allows us to adjust
this logic based on type of asset, prior knowledge of the site, or other
signals. For example, the other use case is progressive rendering of images
- i.e. using flow control to fetch image layers and having control over
where and when to stop. Concretely, we could fetch first X KB, which may
provide a reasonable low-res preview of the asset, and then continue
fetching subsequent layers until some condition is met (end of file, or max
resolution of device is reached -- if you're a 2x device, don't fetch the
3x layers). I'm intentionally skipping over the image container discussions
here, since that's a separate conversation.. But I'll note that many sites
are already trying to provide this sort of experience, except through
rather poor implementation: inline low-res asset, render that, then
initiate a separate XHR to fetch image and replace with high-res version.
Needless to say, we can do much better, and there is strong interest in
providing this sort of functionality natively...

--

In short, there are interesting cases where the initiator of the stream may
want to control the initial window size: to decrease it, and perhaps even
to increase it in some cases. There are multiple ways to achieve this, but
one plausible strategy would be to allow an optional
SETTINGS_INITIAL_WINDOW_SIZE
payload in the headers frame, not unlike the optional priority field --
minimal overhead, no races between initiating the request and
WINDOW_UPDATE, etc.

Thoughts?

Ilya

[1]
https://docs.google.com/document/d/1wck0tFTiibKzZDuBeyK0lrKTKsZ5pqfvcR__1r2m834/edit
[2] http://lists.w3.org/Archives/Public/public-web-perf/2013Aug/0010.html
[3] https://developers.google.com/speed/pagespeed/optimization
[4]
http://www.webpagetest.org/result/130715_PZ_08063384bd76cd2206a1b39e8678e438/3/details/

---

From: Mark Nottingham <[email-masked]>
Date: Wed, 9 Oct 2013 16:41:02 -0700

Hi Ilya,

I've created an issue for this:
  https://github.com/http2/http2-spec/issues/261

Not sure if we'll get to discussing it at the meeting this week.

Cheers,

On 08/10/2013, at 4:14 PM, Ilya Grigorik <[email-masked]> wrote:

> SETTINGS allows us to set a default starting window for all streams + update this default later. However, I think there are compelling cases where it would be beneficial to allow different streams to start with a different initial window size (similar to optional priority in headers frame). Given that this would effectively be a new feature... let me try to motivate why the extra complexity is worth it based on some of the recent work I've been involved with: 
> 
> We want to make browser pre{fetch,rendering} smarter [1,2]. Meaning, we want to be able to discover critical page resources as early as possible and kick off fetches for those assets also. However, since some of these assets are large (and there can be many of them), we need to control how much data we fetch (e.g. if we are too aggressive we may slow down the host page, and/or incur high overhead for the user). 
> 
> A concrete example of this is the work we've been experimenting with in PageSpeed [3], where we are aggressively inlining CSS/JS and even doing "page splits": the rewriter effectively creates two pages, the first is the "prefetch friendly" version which fits in < 15KB (one RTT), and second is the diff that fills in the rest once the navigation is triggered via an XHR -- if that sounds crazy, then that's not too far from the truth. That said, we have a working prototype, and it's showing impressive results.. For example, we can take a mobile wikipedia page, which currently takes 9s (!!!) to first render (on a 3G profile), down to 1.6s [3] by using the combination of prefetch + page split strategies.
> 
> What does flow control have to do this with all this? When issuing a prefetch, we would like to be able to open a request with a lower initial window (e.g. 15KB), and fetch just the head of the document - in most cases, this will allow us to discover the critical resources, initiate requests for them, etc. Then, if the navigation is triggered, we would just increment the window and "resume" the stream to get the rest of the page... This eliminates the need for server rewriting / page splitting, which has a lot of gnarly edge conditions. Handling this at the transport layer would make it much, much simpler and more powerful.
> 
> (Yes, we could lower the initial window for all streams, but that's counter productive in majority of cases... we'd just end up sending a lot more WINDOW_UPDATE frames for non-prefetch request.)
> 
> Further, the ability to set a custom window size also allows us to adjust this logic based on type of asset, prior knowledge of the site, or other signals. For example, the other use case is progressive rendering of images - i.e. using flow control to fetch image layers and having control over where and when to stop. Concretely, we could fetch first X KB, which may provide a reasonable low-res preview of the asset, and then continue fetching subsequent layers until some condition is met (end of file, or max resolution of device is reached -- if you're a 2x device, don't fetch the 3x layers). I'm intentionally skipping over the image container discussions here, since that's a separate conversation.. But I'll note that many sites are already trying to provide this sort of experience, except through rather poor implementation: inline low-res asset, render that, then initiate a separate XHR to fetch image and replace with high-res version. Needless to say, we can do much better, and there is strong interest in providing this sort of functionality natively...
> 
> --
> 
> In short, there are interesting cases where the initiator of the stream may want to control the initial window size: to decrease it, and perhaps even to increase it in some cases. There are multiple ways to achieve this, but one plausible strategy would be to allow an optional SETTINGS_INITIAL_WINDOW_SIZE payload in the headers frame, not unlike the optional priority field -- minimal overhead, no races between initiating the request and WINDOW_UPDATE, etc. 
> 
> Thoughts?
> 
> Ilya
> 
> [1] https://docs.google.com/document/d/1wck0tFTiibKzZDuBeyK0lrKTKsZ5pqfvcR__1r2m834/edit
> [4] http://www.webpagetest.org/result/130715_PZ_08063384bd76cd2206a1b39e8678e438/3/details/

--
Mark Nottingham   http://www.mnot.net/

---

From: Ilya Grigorik <[email-masked]>
Date: Wed, 9 Oct 2013 20:04:23 -0700

Thanks Mark.

On Wed, Oct 9, 2013 at 4:41 PM, Mark Nottingham <[email-masked]> wrote:

> > (Yes, we could lower the initial window for all streams, but that's
> counter productive in majority of cases... we'd just end up sending a lot
> more WINDOW_UPDATE frames for non-prefetch request.)
> >
> > Further, the ability to set a custom window size also allows us to
> adjust this logic based on type of asset, prior knowledge of the site, or
> other signals. For example, the other use case is progressive rendering of
> images - i.e. using flow control to fetch image layers and having control
> over where and when to stop. Concretely, we could fetch first X KB, which
> may provide a reasonable low-res preview of the asset, and then continue
> > In short, there are interesting cases where the initiator of the stream
> may want to control the initial window size: to decrease it, and perhaps
> even to increase it in some cases. There are multiple ways to achieve this,
> but one plausible strategy would be to allow an optional
> SETTINGS_INITIAL_WINDOW_SIZE payload in the headers frame, not unlike the
> optional priority field -- minimal overhead, no races between initiating
> the request and WINDOW_UPDATE, etc.
> >
> > Thoughts?
> >
> > Ilya
> >
> > [1]
> > [2]
> http://lists.w3.org/Archives/Public/public-web-perf/2013Aug/0010.html

---

From: Patrick McManus <[email-masked]>
Date: Wed, 9 Oct 2013 22:21:28 -0700

Hey Ilya,

So Firefox has been taking exactly this approach with both spdy/3.[+] and
http/2 already. We use a small initial window setting that is meant to
apply to pushes - and we reusme them in much the same manner as you
describe. For pulls we want to use large windows so we simply pipeline a
window update in the same SSL data record that contains the HEADER. While
there is a theoretical race there it in effect can't actually be delayed
because of the way SSL frames everything atomically; it hasn't ever been a
problem and no servers have complained to me (yet) about the extra frame
callback overhead. I actually expected to have some interop problems with
this strategy in the early days - but it hasn't been a problem at all.

So imo the use case is already well served by the draft as is.. what's left
is whether saving 8 extra bytes for the pat/ilya use case is interesting
and worth the complexity.. I'd be happy with it of course, but I don't
realistically think it is necessary. ymmv.

-P

On Tue, Oct 8, 2013 at 4:14 PM, Ilya Grigorik <[email-masked]> wrote:

> frame). Given that this would effectively be a new feature... let me try
> to motivate why the extra complexity is worth it based on some of the
> recent work I've been involved with:
>
> 3x layers). I'm intentionally skipping over the image container
> discussions here, since that's a separate conversation.. But I'll note that
> many sites are already trying to provide this sort of experience, except
> through rather poor implementation: inline low-res asset, render that, then
> but one plausible strategy would be to allow an optional SETTINGS_INITIAL_WINDOW_SIZE

---

From: Ilya Grigorik <[email-masked]>
Date: Wed, 9 Oct 2013 23:26:26 -0700

Interesting, thanks Patrick. Curious, when you say "small initial window",
what is the value that you're currently using? Looking at
https://hg.mozilla.org/try/rev/f77c2a53735e, it looks like its 64KB (aka,
kDefaultRwin)? Great to hear that [HEADERS, WINDOW_UPDATE] combo is working
out so far. Agreed, 8 bytes may not be much, but that's still 8 extra bytes
per stream. And if all the popular clients end up going with a strategy
like the one you've described, perhaps it's worth entertaining...

On Wed, Oct 9, 2013 at 10:21 PM, Patrick McManus <[email-masked]>wrote:

> So imo the use case is already well served by the draft as is.. what's
> left is whether saving 8 extra bytes for the pat/ilya use case is
> interesting and worth the complexity.. I'd be happy with it of course, but
> I don't realistically think it is necessary. ymmv.
>
> -P
>
>
>
>