Below are some tests comparing the speed of the Adobe Analytics API to the Google Analytics API. To do the comparison, we’ve looked at two sites – one small and one quite large – that are both running both Adobe Analytics and Google Analytics. To keep the comparison, well, comparable, the queries stick to standard dimensions and metrics so that the “same” data is being pulled from both systems. The queries all pull for the last 365 days.
This is a test comparing the speed of the Adobe Analytics Reporting API to the Google Analytics Reporting API. A few notes about that:
This is focused on the free version of Google Analytics; in the case of the large site, that means sampling comes into play, and, to make it an apples-to-apples comparison, the query uses the anti_sample = TRUE flag in googleAnalyticsR. That’s, basically, a bit of a hack to work around Google Analytics sampling. That shows in the results.
A separate comparison could be comparing the crunching of Adobe Analytics data feed data to Google Analytics 360 data that has been pushed into BigQuery. That’s wayyyy beyond the scope of this assessment, though.
The current test just runs each query once. Eventually, I may update this to run each query multiple times (and possibly try at different days of the week and times of day) to get a more robust set of data. But, I’m starting simple! I haven’t included the setup code here, as that’s a little tedious. But, I’ve tried to include all of the salient code for transparency purposes.
The Queries
This is certainly something that can be fiddled with a bit, but, for the initial test, I set up two queries:
- Daily unique visitors (users), visits (sessions), and page views (pageviews)
- Visits (sessions) broken down by device type and browser
For now, I haven’t added any segments to either query – either inline (dynamic) or named. But, that may be something to add in the future.
The functions below can then be run on different accounts to make these queries:
## COMPARISON #1: DAILY METRICS
# Adobe Analytics
daily_data_aa <- function(rsid, date_from, date_to){
aa_data <- QueueOvertime(rsid,
date.from = date_from,
date.to = date_to,
metrics = c("uniquevisitors", "visits", "pageviews"),
date.granularity = "day")
}
# Google Analytics
# This includes the "unsampled" flag to ensure more of an apples-to-apples comparison
daily_data_ga <- function(view_id, date_from, date_to){
ga_data <- google_analytics_4(view_id,
date_range = c(date_from, date_to),
metrics = c("users", "sessions", "pageviews"),
dimensions = "date",
anti_sample = TRUE)
}
## COMPARISON #2: DEVICE TYPE + BROWSER AGGREGATION
# Adobe Analytics
ranked_data_aa <- function(rsid, date_from, date_to){
aa_data <- QueueRanked(rsid,
date.from = date_from,
date.to = date_to,
metrics = "visits",
elements = c("mobiledevicetype", "browsertype"),
top = c(25,25))
}
# Google Analytics
# This includes the "unsampled" flag to ensure more of an apples-to-apples comparison. One
# extra step is required here to get the data to come back similarly -- specifying the order.
order_ga <- order_type("sessions", sort_order = "DESCENDING", orderType = "VALUE")
ranked_data_ga <- function(view_id, date_from, date_to){
ga_data <- google_analytics_4(view_id,
date_range = c(date_from, date_to),
metrics = "sessions",
dimensions = c("deviceCategory", "browser"),
order = order_ga,
anti_sample = TRUE)
}
Small Site First!
We’ll start with a small site. The first query is the daily data for 365 days. A bit of the data itself is included below to show that they two platforms return comparable results. All that is being recorded for the time is the time to actually make and retrieve the results of the query.
Adobe Analytics: Daily Trend - Small Site
# Adobe Analytics
time_daily_aa <- system.time(aa_data <- daily_data_aa(aa_rsid, start_date_year, end_date))
[1] "Requesting URL attempt #1"
[1] "Requesting URL attempt #2"
[1] "Received overtime report."
aa_data <- aa_data %>%
select(datetime, uniquevisitors, visits, pageviews) %>%
mutate(datetime = as.Date(datetime))
kable(head(aa_data))
| 2016-07-12 |
1493 |
1600 |
2156 |
| 2016-07-13 |
1484 |
1597 |
2124 |
| 2016-07-14 |
1527 |
1608 |
2163 |
| 2016-07-15 |
1108 |
1187 |
1572 |
| 2016-07-16 |
338 |
365 |
494 |
| 2016-07-17 |
542 |
583 |
779 |
Google Analytics: Daily Trend - Small Site
# Google Analytics
time_daily_ga <- system.time(ga_data <- daily_data_ga(ga_view_id, start_date_year, end_date))
kable(head(ga_data))
| 2016-07-12 |
1507 |
1628 |
2188 |
| 2016-07-13 |
1479 |
1620 |
2126 |
| 2016-07-14 |
1505 |
1596 |
2132 |
| 2016-07-15 |
1096 |
1187 |
1589 |
| 2016-07-16 |
335 |
365 |
490 |
| 2016-07-17 |
549 |
598 |
786 |
This query took 13.12 seconds for the Adobe Analytics API and 1.66 seconds for the Google Analytics API.
Now, let’s pull the device type and browser data – also for a year.
Adobe Analytics: Ranked Data - Small Site
# Adobe Analytics
time_ranked_aa <- system.time(aa_data <- ranked_data_aa(aa_rsid, start_date_year, end_date))
[1] "Requesting URL attempt #1"
[1] "Requesting URL attempt #2"
[1] "Received ranked report."
# We're not counting this in the time calculation -- just making it a bit more apples-to-apples with
# Google Analytics.
aa_data <- aa_data %>%
arrange(-visits) %>%
mutate(mobiledevicetype = ifelse(mobiledevicetype=="Other", "Desktop", mobiledevicetype)) %>%
select(mobiledevicetype, browsertype, visits)
kable(head(aa_data))
| Desktop |
Google |
284833 |
| Desktop |
Microsoft |
60961 |
| Desktop |
Mozilla |
39910 |
| Mobile Phone |
Apple |
16831 |
| Desktop |
Apple |
13570 |
| Mobile Phone |
Google |
13483 |
Google Analytics: Ranked Data - Small Site
# Google Analytics
time_ranked_ga <- system.time(ga_data <- ranked_data_ga(ga_view_id, start_date_year, end_date))
kable(head(ga_data))
| desktop |
Chrome |
284983 |
| desktop |
Internet Explorer |
50327 |
| desktop |
Firefox |
39297 |
| mobile |
Safari |
14351 |
| mobile |
Chrome |
13939 |
| desktop |
Safari |
13184 |
This query took 12.8 seconds for the Adobe Analytics API and 1.59 seconds for the Google Analytics API.
Now, A Much Larger Site
This is a much larger site – primarily an Adobe Analytics shop, but they also have Google Analytics implemented (lightly), so it suits our purposes. Again, the first query is the daily data for 365 days. We’re not going to show the code here, because it’s identical to the code above.
Adobe Analytics: Daily Trend - Large Site
[1] "Requesting URL attempt #1"
[1] "Requesting URL attempt #2"
[1] "Requesting URL attempt #3"
[1] "Received overtime report."
| 2016-07-12 |
90375 |
114619 |
692623 |
| 2016-07-13 |
97333 |
122789 |
737202 |
| 2016-07-14 |
94911 |
119724 |
708964 |
| 2016-07-15 |
76312 |
94919 |
547828 |
| 2016-07-16 |
11084 |
12459 |
70326 |
| 2016-07-17 |
9013 |
10003 |
48970 |
Google Analytics: Daily Trend - Large Site
| 2016-07-12 |
92946 |
122265 |
711739 |
| 2016-07-13 |
99901 |
130705 |
740745 |
| 2016-07-14 |
97663 |
127825 |
721988 |
| 2016-07-15 |
78651 |
101623 |
564974 |
| 2016-07-16 |
11226 |
12812 |
71551 |
| 2016-07-17 |
9145 |
10250 |
49598 |
This query took 22.55 seconds for the Adobe Analytics API and 2.15 seconds for the Google Analytics API.
Now, let’s pull the device type and browser data – also for a year.
Adobe Analytics: Ranked Data - Large Site
[1] "Requesting URL attempt #1"
[1] "Requesting URL attempt #2"
[1] "Received ranked report."
| Desktop |
Microsoft |
14624108 |
| Desktop |
Google |
13528832 |
| Desktop |
Mozilla |
4192844 |
| Tablet |
Apple |
1132420 |
| Desktop |
Apple |
734011 |
| Mobile Phone |
Apple |
298861 |
Google Analytics: Ranked Data - Large Site
| desktop |
Chrome |
14255211 |
| desktop |
Internet Explorer |
13774114 |
| desktop |
Firefox |
4308947 |
| desktop |
Edge |
1522778 |
| tablet |
Safari |
1174571 |
| desktop |
Safari |
754205 |
This query took 11.5 seconds for the Adobe Analytics API and 80.47 seconds for the Google Analytics API.
Results Summary
Below is a summary of the results.
| Small Site: Daily Trends |
13.1 |
1.7 |
| Small Site: Ranked Values |
12.8 |
1.6 |
| Large Site: Daily Trends |
22.5 |
2.1 |
| Large Site: Ranked Values |
11.5 |
80.5 |
Google Analytics got killed on the ranked data. For a large site, and with the “anti-sampling” flag being used in the query, that is because there were many calls to the API. If the site was a GA360 site with a custom table set up, it would have come back much faster (but custom tables only go back 30 days, so, even with GA360, this may be a tough one.)
LS0tCnRpdGxlOiAiQWRvYmUgQW5hbHl0aWNzIC8gR29vZ2xlIEFuYWx5dGljcyBBUEkgU3BlZWQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkJlbG93IGFyZSBzb21lIHRlc3RzIGNvbXBhcmluZyB0aGUgc3BlZWQgb2YgdGhlIEFkb2JlIEFuYWx5dGljcyBBUEkgdG8gdGhlIEdvb2dsZSBBbmFseXRpY3MgQVBJLiBUbyBkbyB0aGUgY29tcGFyaXNvbiwgd2UndmUgbG9va2VkIGF0IHR3byBzaXRlcyAtLSBvbmUgc21hbGwgYW5kIG9uZSBxdWl0ZSBsYXJnZSAtLSB0aGF0IGFyZSBib3RoIHJ1bm5pbmcgYm90aCBBZG9iZSBBbmFseXRpY3MgYW5kIEdvb2dsZSBBbmFseXRpY3MuIFRvIGtlZXAgdGhlIGNvbXBhcmlzb24sIHdlbGwsIGNvbXBhcmFibGUsIHRoZSBxdWVyaWVzIHN0aWNrIHRvIHN0YW5kYXJkIGRpbWVuc2lvbnMgYW5kIG1ldHJpY3Mgc28gdGhhdCB0aGUgInNhbWUiIGRhdGEgaXMgYmVpbmcgcHVsbGVkIGZyb20gYm90aCBzeXN0ZW1zLiBUaGUgcXVlcmllcyBhbGwgcHVsbCBmb3IgdGhlIGxhc3QgMzY1IGRheXMuCgpUaGlzIGlzIGEgdGVzdCBjb21wYXJpbmcgdGhlIHNwZWVkIG9mIHRoZSBBZG9iZSBBbmFseXRpY3MgUmVwb3J0aW5nIEFQSSB0byB0aGUgR29vZ2xlIEFuYWx5dGljcyBSZXBvcnRpbmcgQVBJLiBBIGZldyBub3RlcyBhYm91dCB0aGF0OgoKKiBUaGlzIGlzIGZvY3VzZWQgb24gdGhlIGZyZWUgdmVyc2lvbiBvZiBHb29nbGUgQW5hbHl0aWNzOyBpbiB0aGUgY2FzZSBvZiB0aGUgbGFyZ2Ugc2l0ZSwgdGhhdCBtZWFucyBzYW1wbGluZyBjb21lcyBpbnRvIHBsYXksIGFuZCwgdG8gbWFrZSBpdCBhbiBhcHBsZXMtdG8tYXBwbGVzIGNvbXBhcmlzb24sIHRoZSBxdWVyeSB1c2VzIHRoZSBgYW50aV9zYW1wbGUgPSBUUlVFYCBmbGFnIGluIGBnb29nbGVBbmFseXRpY3NSYC4gVGhhdCdzLCBiYXNpY2FsbHksIGEgYml0IG9mIGEgaGFjayB0byB3b3JrIGFyb3VuZCBHb29nbGUgQW5hbHl0aWNzIHNhbXBsaW5nLiBUaGF0IHNob3dzIGluIHRoZSByZXN1bHRzLgoKKiBBIHNlcGFyYXRlIGNvbXBhcmlzb24gY291bGQgYmUgY29tcGFyaW5nIHRoZSBjcnVuY2hpbmcgb2YgQWRvYmUgQW5hbHl0aWNzIGRhdGEgZmVlZCBkYXRhIHRvIEdvb2dsZSBBbmFseXRpY3MgMzYwIGRhdGEgdGhhdCBoYXMgYmVlbiBwdXNoZWQgaW50byBCaWdRdWVyeS4gVGhhdCdzIHdheXl5eSBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgYXNzZXNzbWVudCwgdGhvdWdoLgoKVGhlIGN1cnJlbnQgdGVzdCBqdXN0IHJ1bnMgZWFjaCBxdWVyeSBvbmNlLiBFdmVudHVhbGx5LCBJIG1heSB1cGRhdGUgdGhpcyB0byBydW4gZWFjaCBxdWVyeSBtdWx0aXBsZSB0aW1lcyAoYW5kIHBvc3NpYmx5IHRyeSBhdCBkaWZmZXJlbnQgZGF5cyBvZiB0aGUgd2VlayBhbmQgdGltZXMgb2YgZGF5KSB0byBnZXQgYSBtb3JlIHJvYnVzdCBzZXQgb2YgZGF0YS4gQnV0LCBJJ20gc3RhcnRpbmcgc2ltcGxlISBJIGhhdmVuJ3QgaW5jbHVkZWQgdGhlIHNldHVwIGNvZGUgaGVyZSwgYXMgdGhhdCdzIGEgbGl0dGxlIHRlZGlvdXMuIEJ1dCwgSSd2ZSB0cmllZCB0byBpbmNsdWRlIGFsbCBvZiB0aGUgc2FsaWVudCBjb2RlIGZvciB0cmFuc3BhcmVuY3kgcHVycG9zZXMuCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCiMgR2V0IEdvb2dsZSBBbmFseXRpY3MgY3JlZGVudGlhbHMgYW5kIHNldCB0aGVtLiBUaGlzIGlzIGp1c3QgZm9yIHRoZSB1c2VyIHJ1bm5pbmcKIyB0aGUgZmlsZSBhbmQgd2lsbCB3b3JrIGFjcm9zcyBtdWx0aXBsZSBzaXRlcy4gRm9yIEFkb2JlLCBzZXBhcmF0ZSBjcmVkZW50aWFscwojIGFyZSBuZWVkZWQgZm9yIGVhY2ggc2l0ZS4KZ2FfY2xpZW50X2lkIDwtIFN5cy5nZXRlbnYoIkdBX0NMSUVOVF9JRCIpCmdhX2NsaWVudF9zZWNyZXQgPC0gU3lzLmdldGVudigiR0FfQ0xJRU5UX1NFQ1JFVCIpCgpvcHRpb25zKGdvb2dsZUF1dGhSLmNsaWVudF9pZCA9IGdhX2NsaWVudF9pZCkKb3B0aW9ucyhnb29nbGVBdXRoUi5jbGllbnRfc2VjcmV0ID0gZ2FfY2xpZW50X3NlY3JldCkgCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnb29nbGVBbmFseXRpY3NSKQpsaWJyYXJ5KFJTaXRlQ2F0YWx5c3QpCmxpYnJhcnkoa25pdHIpCgplbmRfZGF0ZSA8LSBTeXMuRGF0ZSgpIC0gMQpzdGFydF9kYXRlX3llYXIgPC0gZW5kX2RhdGUtMzY0CgojIEF1dGhvcml6ZSBHb29nbGUgQW5hbHl0aWNzCmdhX2F1dGgoKQoKIyBJbml0aWFsaXplIGEgZGF0YSBmcmFtZSB0aGF0IHdpbGwgbG9nIGFsbCB0aGUgcmVzdWx0cwpyZXN1bHRzX2xvZyA8LSBkYXRhLmZyYW1lKHRlc3RuYW1lID0gY2hhcmFjdGVyKCksIGFhX3RpbWUgPSBudW1lcmljKCksIGdhX3RpbWUgPSBudW1lcmljKCksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmBgYAoKIyMgVGhlIFF1ZXJpZXMKClRoaXMgaXMgY2VydGFpbmx5IHNvbWV0aGluZyB0aGF0IGNhbiBiZSBmaWRkbGVkIHdpdGggYSBiaXQsIGJ1dCwgZm9yIHRoZSBpbml0aWFsIHRlc3QsIEkgc2V0IHVwIHR3byBxdWVyaWVzOgoKKiBEYWlseSAqKnVuaXF1ZSB2aXNpdG9ycyoqICh1c2VycyksICoqdmlzaXRzKiogKHNlc3Npb25zKSwgYW5kICoqcGFnZSB2aWV3cyoqIChwYWdldmlld3MpCiogKipWaXNpdHMqKiAoc2Vzc2lvbnMpIGJyb2tlbiBkb3duIGJ5ICoqZGV2aWNlIHR5cGUqKiBhbmQgKipicm93c2VyKioKCkZvciBub3csIEkgaGF2ZW4ndCBhZGRlZCBhbnkgc2VnbWVudHMgdG8gZWl0aGVyIHF1ZXJ5IC0tIGVpdGhlciBpbmxpbmUgKGR5bmFtaWMpIG9yIG5hbWVkLiBCdXQsIHRoYXQgbWF5IGJlIHNvbWV0aGluZyB0byBhZGQgaW4gdGhlIGZ1dHVyZS4KClRoZSBmdW5jdGlvbnMgYmVsb3cgY2FuIHRoZW4gYmUgcnVuIG9uIGRpZmZlcmVudCBhY2NvdW50cyB0byBtYWtlIHRoZXNlIHF1ZXJpZXM6CgpgYGB7ciBkYXRhX2Z1bmN0aW9ucywgZWNobz1UUlVFfQoKIyMgQ09NUEFSSVNPTiAjMTogREFJTFkgTUVUUklDUwoKIyBBZG9iZSBBbmFseXRpY3MKZGFpbHlfZGF0YV9hYSA8LSBmdW5jdGlvbihyc2lkLCBkYXRlX2Zyb20sIGRhdGVfdG8pewogIGFhX2RhdGEgPC0gUXVldWVPdmVydGltZShyc2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZS5mcm9tID0gZGF0ZV9mcm9tLAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZS50byA9IGRhdGVfdG8sCiAgICAgICAgICAgICAgICAgICAgICAgICBtZXRyaWNzID0gYygidW5pcXVldmlzaXRvcnMiLCAidmlzaXRzIiwgInBhZ2V2aWV3cyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZS5ncmFudWxhcml0eSA9ICJkYXkiKQp9CgojIEdvb2dsZSBBbmFseXRpY3MKIyBUaGlzIGluY2x1ZGVzIHRoZSAidW5zYW1wbGVkIiBmbGFnIHRvIGVuc3VyZSBtb3JlIG9mIGFuIGFwcGxlcy10by1hcHBsZXMgY29tcGFyaXNvbgpkYWlseV9kYXRhX2dhIDwtIGZ1bmN0aW9uKHZpZXdfaWQsIGRhdGVfZnJvbSwgZGF0ZV90byl7CiAgZ2FfZGF0YSA8LSBnb29nbGVfYW5hbHl0aWNzXzQodmlld19pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlX3JhbmdlID0gYyhkYXRlX2Zyb20sIGRhdGVfdG8pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldHJpY3MgPSBjKCJ1c2VycyIsICJzZXNzaW9ucyIsICJwYWdldmlld3MiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1lbnNpb25zID0gImRhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFudGlfc2FtcGxlID0gVFJVRSkKfQoKIyMgQ09NUEFSSVNPTiAjMjogREVWSUNFIFRZUEUgKyBCUk9XU0VSIEFHR1JFR0FUSU9OCgojIEFkb2JlIEFuYWx5dGljcwpyYW5rZWRfZGF0YV9hYSA8LSBmdW5jdGlvbihyc2lkLCBkYXRlX2Zyb20sIGRhdGVfdG8pewogIGFhX2RhdGEgPC0gUXVldWVSYW5rZWQocnNpZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZS5mcm9tID0gZGF0ZV9mcm9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlLnRvID0gZGF0ZV90bywKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljcyA9ICJ2aXNpdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtZW50cyA9IGMoIm1vYmlsZWRldmljZXR5cGUiLCAiYnJvd3NlcnR5cGUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcCA9IGMoMjUsMjUpKQp9CgojIEdvb2dsZSBBbmFseXRpY3MKIyBUaGlzIGluY2x1ZGVzIHRoZSAidW5zYW1wbGVkIiBmbGFnIHRvIGVuc3VyZSBtb3JlIG9mIGFuIGFwcGxlcy10by1hcHBsZXMgY29tcGFyaXNvbi4gT25lCiMgZXh0cmEgc3RlcCBpcyByZXF1aXJlZCBoZXJlIHRvIGdldCB0aGUgZGF0YSB0byBjb21lIGJhY2sgc2ltaWxhcmx5IC0tIHNwZWNpZnlpbmcgdGhlIG9yZGVyLgoKb3JkZXJfZ2EgPC0gb3JkZXJfdHlwZSgic2Vzc2lvbnMiLCBzb3J0X29yZGVyID0gIkRFU0NFTkRJTkciLCBvcmRlclR5cGUgPSAiVkFMVUUiKQoKcmFua2VkX2RhdGFfZ2EgPC0gZnVuY3Rpb24odmlld19pZCwgZGF0ZV9mcm9tLCBkYXRlX3RvKXsKICBnYV9kYXRhIDwtIGdvb2dsZV9hbmFseXRpY3NfNCh2aWV3X2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGVfcmFuZ2UgPSBjKGRhdGVfZnJvbSwgZGF0ZV90byksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljcyA9ICJzZXNzaW9ucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltZW5zaW9ucyA9IGMoImRldmljZUNhdGVnb3J5IiwgImJyb3dzZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IG9yZGVyX2dhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFudGlfc2FtcGxlID0gVFJVRSkKfQoKYGBgCgojIyBTbWFsbCBTaXRlIEZpcnN0IQoKYGBge3Igc2V0dXBfc21hbGwsIGluY2x1ZGU9RkFMU0V9CiMgU2V0IHVwIHRoZSB0ZXN0cyBmb3IgdGhlIHNtYWxsIHNpdGUKCiMgQWRvYmUgQW5hbHl0aWNzCmFhX3VzZXJuYW1lIDwtIFN5cy5nZXRlbnYoIkFET0JFX0FQSV9VU0VSTkFNRSIpCmFhX3NlY3JldCA8LSBTeXMuZ2V0ZW52KCJBRE9CRV9BUElfU0VDUkVUIikKYWFfcnNpZCA8LVN5cy5nZXRlbnYoIkFET0JFX1JTSUQiKQoKIyBBdXRob3JpemUgQWRvYmUgQW5hbHl0aWNzClNDQXV0aChhYV91c2VybmFtZSwgYWFfc2VjcmV0KQoKIyBHb29nbGUgQW5hbHl0aWNzCmdhX3ZpZXdfaWQgPC0gU3lzLmdldGVudigiR0FfVklFV19JRCIpCgpgYGAKCldlJ2xsIHN0YXJ0IHdpdGggYSBzbWFsbCBzaXRlLiBUaGUgZmlyc3QgcXVlcnkgaXMgdGhlIGRhaWx5IGRhdGEgZm9yIDM2NSBkYXlzLiBBIGJpdCBvZiB0aGUgZGF0YSBpdHNlbGYgaXMgaW5jbHVkZWQgYmVsb3cgdG8gc2hvdyB0aGF0IHRoZXkgdHdvIHBsYXRmb3JtcyByZXR1cm4gY29tcGFyYWJsZSByZXN1bHRzLiBBbGwgdGhhdCBpcyBiZWluZyByZWNvcmRlZCBmb3IgdGhlIHRpbWUgaXMgdGhlIHRpbWUgdG8gYWN0dWFsbHkgbWFrZSBhbmQgcmV0cmlldmUgdGhlIHJlc3VsdHMgb2YgdGhlIHF1ZXJ5LgoKIyMjIEFkb2JlIEFuYWx5dGljczogRGFpbHkgVHJlbmQgLSBTbWFsbCBTaXRlCgpgYGB7ciBkYWlseV9kYXRhX3NtYWxsX2FhLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0V9CiMgQWRvYmUgQW5hbHl0aWNzCnRpbWVfZGFpbHlfYWEgPC0gc3lzdGVtLnRpbWUoYWFfZGF0YSA8LSBkYWlseV9kYXRhX2FhKGFhX3JzaWQsIHN0YXJ0X2RhdGVfeWVhciwgZW5kX2RhdGUpKQphYV9kYXRhIDwtIGFhX2RhdGEgJT4lIAogIHNlbGVjdChkYXRldGltZSwgdW5pcXVldmlzaXRvcnMsIHZpc2l0cywgcGFnZXZpZXdzKSAlPiUgCiAgbXV0YXRlKGRhdGV0aW1lID0gYXMuRGF0ZShkYXRldGltZSkpCmthYmxlKGhlYWQoYWFfZGF0YSkpCmBgYAoKIyMjIEdvb2dsZSBBbmFseXRpY3M6IERhaWx5IFRyZW5kIC0gU21hbGwgU2l0ZQoKYGBge3IgZGFpbHlfZGF0YV9zbWFsbF9nYSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFfQojIEdvb2dsZSBBbmFseXRpY3MKdGltZV9kYWlseV9nYSA8LSBzeXN0ZW0udGltZShnYV9kYXRhIDwtIGRhaWx5X2RhdGFfZ2EoZ2Ffdmlld19pZCwgc3RhcnRfZGF0ZV95ZWFyLCBlbmRfZGF0ZSkpCmthYmxlKGhlYWQoZ2FfZGF0YSkpCmBgYAoKVGhpcyBxdWVyeSB0b29rICoqYHIgcm91bmQodGltZV9kYWlseV9hYVtbM11dLDIpYCoqIHNlY29uZHMgZm9yIHRoZSBBZG9iZSBBbmFseXRpY3MgQVBJIGFuZCAqKmByIHJvdW5kKHRpbWVfZGFpbHlfZ2FbWzNdXSwyKWAqKiBzZWNvbmRzIGZvciB0aGUgR29vZ2xlIEFuYWx5dGljcyBBUEkuCgpgYGB7ciBsb2dfZGFpbHlfc21hbGwsIGluY2x1ZGU9RkFMU0V9CiMgTG9nIHRoZSByZXN1bHRzCnJlc3VsdHNfbG9nW25yb3cocmVzdWx0c19sb2cpKzEsXSA8LSBjKCJTbWFsbCBTaXRlOiBEYWlseSBUcmVuZHMiLCB0aW1lX2RhaWx5X2FhW1szXV0sIHRpbWVfZGFpbHlfZ2FbWzNdXSkKYGBgCgpOb3csIGxldCdzIHB1bGwgdGhlICoqZGV2aWNlIHR5cGUqKiBhbmQgKipicm93c2VyKiogZGF0YSAtLSBhbHNvIGZvciBhIHllYXIuCgojIyMgQWRvYmUgQW5hbHl0aWNzOiBSYW5rZWQgRGF0YSAtIFNtYWxsIFNpdGUKCmBgYHtyIHJhbmtlZF9kYXRhX3NtYWxsX2FhLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQWRvYmUgQW5hbHl0aWNzCnRpbWVfcmFua2VkX2FhIDwtIHN5c3RlbS50aW1lKGFhX2RhdGEgPC0gcmFua2VkX2RhdGFfYWEoYWFfcnNpZCwgc3RhcnRfZGF0ZV95ZWFyLCBlbmRfZGF0ZSkpCgojIFdlJ3JlIG5vdCBjb3VudGluZyB0aGlzIGluIHRoZSB0aW1lIGNhbGN1bGF0aW9uIC0tIGp1c3QgbWFraW5nIGl0IGEgYml0IG1vcmUgYXBwbGVzLXRvLWFwcGxlcyB3aXRoCiMgR29vZ2xlIEFuYWx5dGljcy4KICBhYV9kYXRhIDwtIGFhX2RhdGEgJT4lIAogICAgYXJyYW5nZSgtdmlzaXRzKSAlPiUgCiAgICBtdXRhdGUobW9iaWxlZGV2aWNldHlwZSA9IGlmZWxzZShtb2JpbGVkZXZpY2V0eXBlPT0iT3RoZXIiLCAiRGVza3RvcCIsIG1vYmlsZWRldmljZXR5cGUpKSAlPiUKICAgIHNlbGVjdChtb2JpbGVkZXZpY2V0eXBlLCBicm93c2VydHlwZSwgdmlzaXRzKQoKa2FibGUoaGVhZChhYV9kYXRhKSkKYGBgCgojIyMgR29vZ2xlIEFuYWx5dGljczogUmFua2VkIERhdGEgLSBTbWFsbCBTaXRlCgpgYGB7ciByYW5rZWRfZGF0YV9zbWFsbF9nYSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEdvb2dsZSBBbmFseXRpY3MKdGltZV9yYW5rZWRfZ2EgPC0gc3lzdGVtLnRpbWUoZ2FfZGF0YSA8LSByYW5rZWRfZGF0YV9nYShnYV92aWV3X2lkLCBzdGFydF9kYXRlX3llYXIsIGVuZF9kYXRlKSkKa2FibGUoaGVhZChnYV9kYXRhKSkKYGBgCgpUaGlzIHF1ZXJ5IHRvb2sgKipgciByb3VuZCh0aW1lX3JhbmtlZF9hYVtbM11dLDIpYCoqIHNlY29uZHMgZm9yIHRoZSBBZG9iZSBBbmFseXRpY3MgQVBJIGFuZCAqKmByIHJvdW5kKHRpbWVfcmFua2VkX2dhW1szXV0sMilgKiogc2Vjb25kcyBmb3IgdGhlIEdvb2dsZSBBbmFseXRpY3MgQVBJLgoKYGBge3IgbG9nX3JhbmtlZF9zbWFsbCwgaW5jbHVkZT1GQUxTRX0KIyBMb2cgdGhlIHJlc3VsdHMKcmVzdWx0c19sb2dbbnJvdyhyZXN1bHRzX2xvZykrMSxdIDwtIGMoIlNtYWxsIFNpdGU6IFJhbmtlZCBWYWx1ZXMiLCB0aW1lX3JhbmtlZF9hYVtbM11dLCB0aW1lX3JhbmtlZF9nYVtbM11dKQpgYGAKCiMjIE5vdywgQSBNdWNoIExhcmdlciBTaXRlCgpgYGB7ciBzZXR1cF9sYXJnZSwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgdXAgdGhlIHRlc3RzIGZvciB0aGUgbGFyZ2VyIHNpdGUKCiMgQWRvYmUgQW5hbHl0aWNzCmFhX3VzZXJuYW1lIDwtIFN5cy5nZXRlbnYoIkFET0JFX0FQSV9VU0VSTkFNRV9NIikKYWFfc2VjcmV0IDwtIFN5cy5nZXRlbnYoIkFET0JFX0FQSV9TRUNSRVRfTSIpCmFhX3JzaWQgPC1TeXMuZ2V0ZW52KCJBRE9CRV9SU0lEX00iKQoKIyBBdXRob3JpemUgQWRvYmUgQW5hbHl0aWNzClNDQXV0aChhYV91c2VybmFtZSwgYWFfc2VjcmV0KQoKIyBHb29nbGUgQW5hbHl0aWNzCmdhX3ZpZXdfaWQgPC0gU3lzLmdldGVudigiR0FfVklFV19JRF9NIikKCmBgYAoKVGhpcyBpcyBhIG11Y2ggbGFyZ2VyIHNpdGUgLS0gcHJpbWFyaWx5IGFuIEFkb2JlIEFuYWx5dGljcyBzaG9wLCBidXQgdGhleSBhbHNvIGhhdmUgR29vZ2xlIEFuYWx5dGljcyBpbXBsZW1lbnRlZCAobGlnaHRseSksIHNvIGl0IHN1aXRzIG91ciBwdXJwb3Nlcy4gQWdhaW4sIHRoZSBmaXJzdCBxdWVyeSBpcyB0aGUgZGFpbHkgZGF0YSBmb3IgMzY1IGRheXMuIFdlJ3JlIG5vdCBnb2luZyB0byBzaG93IHRoZSBjb2RlIGhlcmUsIGJlY2F1c2UgaXQncyBpZGVudGljYWwgdG8gdGhlIGNvZGUgYWJvdmUuCgojIyMgQWRvYmUgQW5hbHl0aWNzOiBEYWlseSBUcmVuZCAtIExhcmdlIFNpdGUKCmBgYHtyIGRhaWx5X2RhdGFfbGFyZ2VfYWEsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgQWRvYmUgQW5hbHl0aWNzCnRpbWVfZGFpbHlfbGFyZ2VfYWEgPC0gc3lzdGVtLnRpbWUoYWFfZGF0YSA8LSBkYWlseV9kYXRhX2FhKGFhX3JzaWQsIHN0YXJ0X2RhdGVfeWVhciwgZW5kX2RhdGUpKQphYV9kYXRhIDwtIGFhX2RhdGEgJT4lIAogIHNlbGVjdChkYXRldGltZSwgdW5pcXVldmlzaXRvcnMsIHZpc2l0cywgcGFnZXZpZXdzKSAlPiUgCiAgbXV0YXRlKGRhdGV0aW1lID0gYXMuRGF0ZShkYXRldGltZSkpCmthYmxlKGhlYWQoYWFfZGF0YSkpCmBgYAoKIyMjIEdvb2dsZSBBbmFseXRpY3M6IERhaWx5IFRyZW5kIC0gTGFyZ2UgU2l0ZQoKYGBge3IgZGFpbHlfZGF0YV9sYXJnZV9nYSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBHb29nbGUgQW5hbHl0aWNzCnRpbWVfZGFpbHlfbGFyZ2VfZ2EgPC0gc3lzdGVtLnRpbWUoZ2FfZGF0YSA8LSBkYWlseV9kYXRhX2dhKGdhX3ZpZXdfaWQsIHN0YXJ0X2RhdGVfeWVhciwgZW5kX2RhdGUpKQprYWJsZShoZWFkKGdhX2RhdGEpKQpgYGAKClRoaXMgcXVlcnkgdG9vayAqKmByIHJvdW5kKHRpbWVfZGFpbHlfbGFyZ2VfYWFbWzNdXSwyKWAqKiBzZWNvbmRzIGZvciB0aGUgQWRvYmUgQW5hbHl0aWNzIEFQSSBhbmQgKipgciByb3VuZCh0aW1lX2RhaWx5X2xhcmdlX2dhW1szXV0sMilgKiogc2Vjb25kcyBmb3IgdGhlIEdvb2dsZSBBbmFseXRpY3MgQVBJLgoKYGBge3IgbG9nX2RhaWx5X2xhcmdlLCBpbmNsdWRlPUZBTFNFfQojIExvZyB0aGUgcmVzdWx0cwpyZXN1bHRzX2xvZ1tucm93KHJlc3VsdHNfbG9nKSsxLF0gPC0gYygiTGFyZ2UgU2l0ZTogRGFpbHkgVHJlbmRzIiwgdGltZV9kYWlseV9sYXJnZV9hYVtbM11dLCB0aW1lX2RhaWx5X2xhcmdlX2dhW1szXV0pCmBgYAoKTm93LCBsZXQncyBwdWxsIHRoZSAqKmRldmljZSB0eXBlKiogYW5kICoqYnJvd3NlcioqIGRhdGEgLS0gYWxzbyBmb3IgYSB5ZWFyLgoKIyMjIEFkb2JlIEFuYWx5dGljczogUmFua2VkIERhdGEgLSBMYXJnZSBTaXRlCgpgYGB7ciByYW5rZWRfZGF0YV9sYXJnZV9hYSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBBZG9iZSBBbmFseXRpY3MKdGltZV9yYW5rZWRfbGFyZ2VfYWEgPC0gc3lzdGVtLnRpbWUoYWFfZGF0YSA8LSByYW5rZWRfZGF0YV9hYShhYV9yc2lkLCBzdGFydF9kYXRlX3llYXIsIGVuZF9kYXRlKSkKCiMgV2UncmUgbm90IGNvdW50aW5nIHRoaXMgaW4gdGhlIHRpbWUgY2FsY3VsYXRpb24gLS0ganVzdCBtYWtpbmcgaXQgYSBiaXQgbW9yZSBhcHBsZXMtdG8tYXBwbGVzIHdpdGgKIyBHb29nbGUgQW5hbHl0aWNzLgogIGFhX2RhdGEgPC0gYWFfZGF0YSAlPiUgCiAgICBhcnJhbmdlKC12aXNpdHMpICU+JSAKICAgIG11dGF0ZShtb2JpbGVkZXZpY2V0eXBlID0gaWZlbHNlKG1vYmlsZWRldmljZXR5cGU9PSJPdGhlciIsICJEZXNrdG9wIiwgbW9iaWxlZGV2aWNldHlwZSkpICU+JQogICAgc2VsZWN0KG1vYmlsZWRldmljZXR5cGUsIGJyb3dzZXJ0eXBlLCB2aXNpdHMpCgprYWJsZShoZWFkKGFhX2RhdGEpKQpgYGAKCiMjIyBHb29nbGUgQW5hbHl0aWNzOiBSYW5rZWQgRGF0YSAtIExhcmdlIFNpdGUKCmBgYHtyIHJhbmtlZF9kYXRhX2xhcmdlX2dhLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBHb29nbGUgQW5hbHl0aWNzCnRpbWVfcmFua2VkX2xhcmdlX2dhIDwtIHN5c3RlbS50aW1lKGdhX2RhdGEgPC0gcmFua2VkX2RhdGFfZ2EoZ2Ffdmlld19pZCwgc3RhcnRfZGF0ZV95ZWFyLCBlbmRfZGF0ZSkpCgpnYV9kYXRhIDwtIGdhX2RhdGEgJT4lIAogIGFycmFuZ2UoLXNlc3Npb25zKQoKa2FibGUoaGVhZChnYV9kYXRhKSkKYGBgCgpUaGlzIHF1ZXJ5IHRvb2sgKipgciByb3VuZCh0aW1lX3JhbmtlZF9sYXJnZV9hYVtbM11dLDIpYCoqIHNlY29uZHMgZm9yIHRoZSBBZG9iZSBBbmFseXRpY3MgQVBJIGFuZCAqKmByIHJvdW5kKHRpbWVfcmFua2VkX2xhcmdlX2dhW1szXV0sMilgKiogc2Vjb25kcyBmb3IgdGhlIEdvb2dsZSBBbmFseXRpY3MgQVBJLgoKYGBge3IgbG9nX3JhbmtlZF9sYXJnZSwgaW5jbHVkZT1GQUxTRX0KIyBMb2cgdGhlIHJlc3VsdHMKcmVzdWx0c19sb2dbbnJvdyhyZXN1bHRzX2xvZykrMSxdIDwtIGMoIkxhcmdlIFNpdGU6IFJhbmtlZCBWYWx1ZXMiLCB0aW1lX3JhbmtlZF9sYXJnZV9hYVtbM11dLCB0aW1lX3JhbmtlZF9sYXJnZV9nYVtbM11dKQpgYGAKCiMjIFJlc3VsdHMgU3VtbWFyeQoKQmVsb3cgaXMgYSBzdW1tYXJ5IG9mIHRoZSByZXN1bHRzLgoKYGBge3IgcmVzdWx0cywgZWNobz1GQUxTRX0KCnJlc3VsdHNfbG9nIDwtIHJlc3VsdHNfbG9nICU+JSAKICBtdXRhdGUoYWFfdGltZSA9IHJvdW5kKGFzLm51bWVyaWMoYWFfdGltZSksMSksIGdhX3RpbWUgPSByb3VuZChhcy5udW1lcmljKGdhX3RpbWUpLDEpKQoKbmFtZXMocmVzdWx0c19sb2cpIDwtIGMoIlF1ZXJ5IiwgIkFkb2JlIEFuYWx5dGljcyBUaW1lIChzZWNvbmRzKSIsICJHb29nbGUgQW5hbHl0aWNzIFRpbWUgKHNlY29uZHMpIikKCmthYmxlKHJlc3VsdHNfbG9nKQoKIyAjIENvbnZlcnQgdG8gbG9uZwojIHJlc3VsdHNfbG9uZyA8LSBnYXRoZXIocmVzdWx0c19sb2csIHBsYXRmb3JtLCB0aW1lLCBgQWRvYmUgQW5hbHl0aWNzIFRpbWUgKHNlY29uZHMpYCwgYEdvb2dsZSBBbmFseXRpY3MgVGltZSAoc2Vjb25kcylgKQojIAojIHJlc3VsdHNfcGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IHJlc3VsdHNfbG9uZywgbWFwcGluZyA9IGFlcyh4PWBRdWVyeWAsIHk9dGltZSkpICsKIyAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKIyAKIyByZXN1bHRzX3Bsb3QKCmBgYAoKR29vZ2xlIEFuYWx5dGljcyBnb3Qga2lsbGVkIG9uIHRoZSByYW5rZWQgZGF0YS4gRm9yIGEgbGFyZ2Ugc2l0ZSwgYW5kIHdpdGggdGhlICJhbnRpLXNhbXBsaW5nIiBmbGFnIGJlaW5nIHVzZWQgaW4gdGhlIHF1ZXJ5LCB0aGF0IGlzIGJlY2F1c2UgdGhlcmUgd2VyZSBtYW55IGNhbGxzIHRvIHRoZSBBUEkuIElmIHRoZSBzaXRlIHdhcyBhIEdBMzYwIHNpdGUgd2l0aCBhIGN1c3RvbSB0YWJsZSBzZXQgdXAsIGl0IHdvdWxkIGhhdmUgY29tZSBiYWNrIG11Y2ggZmFzdGVyIChidXQgY3VzdG9tIHRhYmxlcyBvbmx5IGdvIGJhY2sgMzAgZGF5cywgc28sIGV2ZW4gd2l0aCBHQTM2MCwgdGhpcyBtYXkgYmUgYSB0b3VnaCBvbmUuKQo=