[Resource Timing] Unified Timing v3

Hi all,

Being part of a W3 working group, we're responsible for maintaining the
coherence of the web platform. Our new features should extend the platform
in a consistent and forward-looking manner.

Given this, I'm proposing a third iteration of the Unified Timing API. The
goal is to provide one API that works for the three existing specs
(Navigation, Resource, and User Timing) and also has provisions for future
uses. I realize that Navigation Timing is already pretty finalized, so it
may not be feasible to switch. I've left it in the examples just as a
proof-of-concept.

This iteration draws inspiration from the DOM, since that is a familiar API
and has successfully solved many of the problems we face, including handling
diverse strongly typed objects and lookup of specific classes of entries.

Please let me know how we can improve this further. I'd love to have the
discussion on this mailing list, where we can have the greatest
participation.

Thanks,
James

Significant Changes

   - Entries are strongly typed and inherit from a base class.
   - User Timing is now known as Developer Timing to better reflect its use.


IDLinterface PerformanceEntry {
 readonly attribute DOMString name;
 unsigned long long getDuration();
}

interface PerformanceEntryList {
 readonly attribute unsigned long length;
 PermanceEntries item(in unsigned long index);
}

interface NavigationPerformanceEntry : PerformanceEntry {
 const unsigned short TYPE_NAVIGATE = 0;
 const unsigned short TYPE_RELOAD = 1;
 const unsigned short TYPE_BACK_FORWARD = 2;
 const unsigned short TYPE_RESERVED = 255;

 readonly attribute unsigned long long navigationStart;
 readonly attribute unsigned long long unloadEventStart;
 readonly attribute unsigned long long unloadEventEnd;
 readonly attribute unsigned long long redirectStart;
 readonly attribute unsigned long long redirectEnd;
 readonly attribute unsigned long long fetchStart;
 readonly attribute unsigned long long domainLookupStart;
 readonly attribute unsigned long long domainLookupEnd;
 readonly attribute unsigned long long connectStart;
 readonly attribute unsigned long long connectEnd;
 readonly attribute unsigned long long secureConnectionStart;
 readonly attribute unsigned long long requestStart;
 readonly attribute unsigned long long responseStart;
 readonly attribute unsigned long long responseEnd;
 readonly attribute unsigned long long domLoading;
 readonly attribute unsigned long long domInteractive;
 readonly attribute unsigned long long domContentLoadedEventStart;
 readonly attribute unsigned long long domContentLoadedEventEnd;
 readonly attribute unsigned long long domComplete;
 readonly attribute unsigned long long loadEventStart;
 readonly attribute unsigned long long loadEventEnd;

 readonly attribute unsigned short type;
 readonly attribute unsigned short redirectCount;
}

interface ResourcePerformanceEntry : PerformanceEntry {
 const unsigned short INITIATOR_OTHER = 0;
 const unsigned short INITIATOR_LINK = 1;
 const unsigned short INITIATOR_CSS = 2;
 const unsigned short INITIATOR_SCRIPT = 3;
 const unsigned short INITIATOR_IMAGE = 4;
 const unsigned short INITIATOR_OBJECT = 5;
 const unsigned short INITIATOR_SUBDOCUMENT = 6;
 const unsigned short INITIATOR_XMLHTTPREQUEST = 7;
 const unsigned short INITIATOR_EMBED = 8;
 const unsigned short INITIATOR_AUDIO = 9;
 const unsigned short INITIATOR_VIDEO = 10;
 const unsigned short INITIATOR_SVG = 11;

 readonly attribute unsigned short type;

 readonly attribute unsigned long long resourceStart;
 readonly attribute unsigned long long redirectStart;
 readonly attribute unsigned long long redirectEnd;
 readonly attribute unsigned long long fetchStart;
 readonly attribute unsigned long long domainLookupStart;
 readonly attribute unsigned long long domainLookupEnd;
 readonly attribute unsigned long long connectStart;
 readonly attribute unsigned long long connectEnd;
 readonly attribute unsigned long long secureConnectionStart;
 readonly attribute unsigned long long requestStart;
 readonly attribute unsigned long long responseStart;
 readonly attribute unsigned long long responseEnd;
}

interface DeveloperMark : PerformanceEntry {
 readonly attribute unsigned long long time;
}

interface DeveloperMeasure : PerformanceEntry {
 void addMark(in DOMString name);
}

interface Performance {
 const unsigned short PERF_NAVIGATION = 0;
 const unsigned short PERF_RESOURCE = 1;
 const unsigned short PERF_DEVELOPER_MARK = 2;
 const unsigned short PERF_DEVELOPER_MEASURE = 3;

 void enable(in unsigned short metric);
 void disable(in unsigned short metric);

 PerformanceEntryList getEntriesByType(in unsigned short metric);
 PerformanceEntryList getEntriesByName(in DOMString name);

 void setBufferCapacity(in unsigned long length);
 void clearBuffer();
 readonly attribute unsigned long bufferLength;
 attribute EventListener onentryadded;

 DeveloperMark createMark(in DOMString name);
 DeveloperMeasure createMeasure(in DOMString name);
}

DescriptiongetDuration()Returns the difference between the first and last
marks. Returns 0 if less than 2 marks are in the object.

addMark()
Records the current time in a new mark with the given name. If the mark
already exists, overwrite it.

enable()
Adds the metric type to the list of entries that will be recorded in the
buffer.

disable()
Removes the metric type to the list of entries that will be recorded in the
buffer.

getEntriesByType()
Returns all entries in the buffer that match the specified type.

getEntriesByName()
Returns all entries in the buffer that match the specified name.

setBufferCapacity()
Resizes the buffer to the specified size. If the size is smaller than the
current, the oldest entries are dropped.

clearBuffer()
Removes all items from the buffer.

bufferLength
Returns the number of items currently in the buffer.

onentryadded
Fired every time a new metric is sent to the buffer.

createMark()
Returns a new DeveloperMark with the specified name and places it in the
buffer.

createMeasure()
Returns a new DeveloperMeasure with the specified name and places it in the
buffer.

Processing ModelBy default, PERF_NAVIGATION, PERF_RESOURCE,
PERF_DEVELOPER_MARK, and PERF_DEVELOPER_MEASURE are enabled. That’s all of
them right now, but that won’t hold in the future.

The buffer is a queue. If a new entry is added and the capacity is exceeded,
the oldest entry evicted. The buffer must have 1 reserve slot, which is not
exposed to the developer.

For UA generated entries:
1. Wait until the metric is fully populated.
2. If the metric’s type is not enabled, abort these steps.
3. Add the entry to the first empty slot in the buffer. If the buffer is
full, add it to the reserve slot.
4. If the size of the buffer exceeds the capacity, remove the first element.
5. Fire onentryadded.

For developer generated entry:
<Start from step #2 above>

Writer Examples (Developer Timing)createMark(“doneLoadingPage”);
-> { name: “doneLoadingPage”, time: 123 }

metadataMark = createMark(“xhrErrorOccurred”);
-> { name: “xhrErrorOccurred”, time: 123 }
metadataMark.errorCode = 7;
metadataMark;
-> { errorCode: 7, name: “xhrErrorOccurred”, time: 123 }

measure = createMeasure(“fetchInbox”);
-> { name: “fetchInbox” }
measure.addMark(“start”);
measure.count = 0;
measure.addMark(“fetchedEmail” + ++measure.count);
… (x3)
measure.addMark(“done”);
measure;
-> { count: 3, done: 30, fetchedEmail0: 10, fetchedEmail1: 20,
fetchedEmail2: 30, name: “fetchEmail”, start: 0 }

Reader ExamplesgetMarksByType(PERF_NAVIGATION); (or
getMarksById(“Document”);)
-> [{ connectEnd: 9, connectStart: 8, …, name: “Document”, …
secureConnectionStart: 0 }]

getMarksByType(PERF_DEVELOPER_MARK);
-> [{ name: “doneLoadingPage”, time: 123 }, { errorCode: 7, name:
“xhrErrorOccurred”, time: 123 }]

getEntriesById(“http://mydoma.in/myimage.html”);
-> [{ connectEnd: 37, …, name: “http://mydoma.in/myimage.html”, …, type: 4
}]

Future ExampleDebug a WebGL Game LoadThis is just a proof-of-concept
example. The Unified Timing API is used to measure all of the steps to load
a Javascript game and display the first WebGL frame on the page. Developers
might do this to see why their game loads slowly for some users. Script
Timing and WebGL Timing are merely examples; I'm not proposing they be
implemented as is.

void setUp() {
 performance.enable(PERF_SCRIPT);
 performance.enable(PERF_WEBGL);
 performance.setBufferCapacity(1000);
 document.onload = onload;
}

void onload() {
 initGame();
 performance.onentryadded = onadded;
}

void initGame() {
 var measure = performance.createMeasure(“InitGame”);
 measure.addMark(“start”);
 …
 measure.addMark(“constructLevel”);
 ...
 measure.addMark(“initGL”);
 …
 measure.addMark(“end”);
}

void onadded(entry) {
 if (typeof entry == “WebGLPerformanceEntry”) {
   // For this example, we’re done when the first frame is drawn.
   performance.onentryadded = null;
   pingBack();
 }
}

void pingBack() {
 var metrics = [];

 metrics.concat(performance.getEntriesByType(PERF_NAVIGATION));

 var resources = performance.getEntriesByType(PERF_RESOURCE);
 var slowest_resource = resources[0];
 for (var i = 1; i < resources.length; ++i) {
   if (resources[i].getDuration() > slowest_resource.getDuration()) {
     slowest_resource = resources[i];
   }
 }
 metrics.append(slowest_resource);

 metrics.concat(performance.getEntriesByType(“http://jsgame.com/game.js”));
 metrics.concat(performance.getEntriesById(“glFlush0”));

 xhr = new XMLHttpRequest(...);
 xhr.open();
 xhr.send(metrics);
}
Graph Generated from Collected Data

Received on Monday, 6 June 2011 22:14:15 UTC