Re: Question about HTTP 2.0 priority

Thank you for your explanation, Amos.  Thanks especially for clarifying
that each assets is its own stream.

You're right that I can use dependencies to implement statically-known
priorities.  Let me see if it's possible to do the same with a more
complicated and dynamic (and realistic) example:

Let's imagine loading a 3D scene.  It contains one room object, 3 avatar
objects (including your own), and 5 pieces of furniture.

To load an object, first a JSON "object description" must be downloaded.
This object description roughly corresponds to the HTML on a web page; it
contains links to the remaining assets: meshes, thumbnail textures, and
high-res textures.  Once the object description JSON is downloaded, all of
the remaining assets are requested in parallel.  The object will not be
visible in WebGL until the meshes and low-res textures are downloaded.
Afterwards, the high-res textures are placed in the scene when they finish
downloading (which could be many seconds or even minutes in the future, as
a scene typically contains 50 MB of high-resolution texture data.)

Calculating priority for a request takes three factors into consideration:
which object the request is for, the type of the asset being loaded, and
for some objects the distance from the camera, per the following table:



Room, My Avatar

Furniture, Other Avatars

Object Descriptions

290

280 - DistanceFromCamera

Meshes, Lo-res Textures

190

180 - DistanceFromCamera

High-res Textures

90

80 - DistanceFromCamera


The loadObject algorithm looks something like the following JavaScript.
Load requests happen in some arbitrary order on the web page (as people
come and go from the 3D scene.)  The web page knows how to calculate the
priorityModifier given the above algorithm.

function loadObject(url, priorityModifier) {
    var object = new Object3D;

    // first we need to load the object description JSON
    return request(url, 200 + priorityModifier)

        // parse it into an object description
        .then(parseObjectDescription)

        .then(function(desc) {
            // optimistically begin fetching all low-priority, high-res
textures
            requestHighResTextures(desc.highResTextures + priorityModifier);

            // then, once the meshes and low-res textures loaded, place the
object in the scene
            return Promise.all([
                requestMeshes(desc.meshes, 190 + priorityModifier),
                requestTextures(desc.lowResTextures, 190 +
priorityModifier),
            ]).then(function() {
                object.makeVisibleInScene();
            });
        });
}

function calledEverySecond() {
    // go through all pending object loads for furniture and other avatars
    // adjust priorities based on distance from camera
}

// call loadObject once for the room, once for your avatar, a few times for
the other avatars, and a few times for the furniture
// the sequence of loadObject calls is not necessarily known in advance


That was a long example, but I hope it demonstrates why it's not entirely
obvious how to build an HTTP/2 dependency graph from what are conceptually
just priorities.

This gets especially hairy when a bunch of low-priority requests depend on
a request that finishes...  if I understand the spec correctly, that will
implicitly bump up the priority of the low-priority requests, which would
be undesirable here.  Low-priority needs to STAY low-priority, even as
higher-priority (aka dependent) requests finish.

Why does HTTP/2 have such a complicated system for priority rather than
SPDY's simple priority integer?  It seems a simple priority value would
make everything so much simpler for both clients and servers.

Thanks again.  I truly appreciate that you took the time to understand my
use case and respond as such.

Chad


On Wed, Oct 1, 2014 at 11:01 PM, Amos Jeffries <squid3@treenet.co.nz> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 2/10/2014 2:12 p.m., Chad Austin wrote:
> > Hi,
> >
> > I am reading the HTTP 2.0 draft, and I wonder whether HTTP 2.0
> > supports implementing simple stream priorities like SPDY does.
> >
> > Our use case is documented in detail at
> >
> http://chadaustin.me/2014/08/web-platform-limitations-xmlhttprequest-priority/
> >
> >
> , but I will summarize here.
> >
> > We have a whole pile of 3D assets that we need to retrieve from
> > various URLs and load into WebGL.  Some of them are higher-priority
> > than others. None of them depend on any other.  For example, we may
> > want to prioritize resources closer to the camera.  We definitely
> > want to prioritize meshes and low-resolution textures over
> > high-resolution textures and animation files.
> >
> > Ideally, our application would immediately issue ALL requests and
> > let the browser's network stack (and HTTP/SPDY/HTTP2 backend)
> > efficiently utilize the socket(s) and bandwidth to transfer
> > high-priority assets before low-priority assets.  However, if there
> > is available bandwidth, we would benefit from receiving
> > low-priority assets in the meantime.
> >
> > We would benefit from a large number of priority bits, but could
> > make it work with as low as 3 bits.  (2 bits would be too few.)
> >
> > How do I map that use case to HTTP 2.0's dependency graph?  At
> > first blush, it seems hard: I'd have to define a stream per
> > priority level, and set dependencies such that all low-priority
> > streams depend on high-priority streams?  And if a high-priority
> > stream finishes, then reset the dependency to another high-priority
> > stream?
> >
>
> Not hard at all. Just reverse your thinking about priority levels.
>
> You have to create a stream per asset in HTTP/2. The priority for the
> stream is assigned based on which *single* asset that stream is being
> used to deliver.
>
> For assiging HTTP/2 priorities to your assets just work out the
> dependency tree they naturally have anyway. Then walk down it
> assigning the root asset the highest priority and each successive
> level gets assigned a dependency on the asset/stream above it in the tree.
>
> If the drawing dependency is something like:
>  map/mesh -> objects -> skeletons -> low-res texture -> high-res texture
>
> The HTTP/2 fetch dependency would be:
>  map-/mesh <- objects <- skeletons <-low-res <- high-res
>
> eg.
>  - everything depends on the map/mesh finishing first.
>  - object descriptions need to finish next,
>  - followed by skeletons as the object they are for finishes,
>  - followed by low-res textures once skeleton+object are finished,
>  - high-res texture for a construct only required after all the
> previous bits are in.
>
> When there is multiple (say 20) high-priority objects/skeletons
> depending on a single low-res texture it is up to you whether you make
> a dependency of the low-res texture on the 1st objects stream ID or
> the 20th objects. If priority is obeyed, depending on the first object
> will allow that and later received objects to a bit render earlier
> incrementally, depending on the last one will make them all wait for
> complete data, but then render as a batch.
>
> HTTP/2 explicitly makes priority optional, so it helps your download
> performance if the requests are ordered in a similar way to how you
> want them prioritized ("by group" as it were). That way even a naive
> intermediary which ignores priority is likely to server the
> most-wanted assets first due purely to their stream ID numbers being
> first.
>
> Amos
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v2.0.22 (MingW32)
>
> iQEcBAEBAgAGBQJULOojAAoJELJo5wb/XPRjoCkIAMsSm78OpqY+HxJXwkBfDRb8
> +2wDSSBuXuTRkCdhkblT2u/5wgsSvfnoZaPAf2dEMa9j2qK2LUgUUiwMN6wIPH38
> WFvq/YLNKMNKecx2r7JvbZ//vvLgyFp5vP1D4mcSpWbf1RY3jKoRd7bfnD7wu/vO
> OOZify8d3Qq5r6Ax2Cj2kkhfuudFwlpg1CsmOd5PNrxan7qjLc3tRYeMB5SJU6W/
> TxmuIq0iLSvdiHDawANk/sI9sesjB7Wblu8WjuZh4+XaQClHRuNv9PS8yFrY/Qzq
> I5chqtT27X+cV8CX4GojjWQi9pFqUksAsnmP2VlKquBeSqDq7tl/1/M/H/VTHhU=
> =ZG/f
> -----END PGP SIGNATURE-----
>
>


-- 
Chad Austin
http://chadaustin.me

Received on Thursday, 2 October 2014 08:08:12 UTC