This analysis compares performance between the odbc package and the RStudio Professional Drivers against the RPostgreSQL package. These tests are compared on small and large tables:

There are many ways to collect data into R from a database. These methods were examined, ranked in order from best performing to least:

odbc connection
con1 <- dbConnect(odbc::odbc(),
Driver = conf$driver,
Database = conf$database,
Port = conf$port,
Server = conf$server,
UID = conf$UID,
PWD = conf$PWD
)
RPostgreSQL connection
con2 <- dbConnect(RPostgreSQL::PostgreSQL(),
dbname = conf$database,
port = conf$port,
host = conf$server,
user = conf$UID,
password = conf$PWD
)
Query functions
Median result (odbc / RPostgreSQL):
- dbSendQuery (4.9 ms / 12.0 ms)
- dbGetQuery (4.9 ms / 12.5 ms)
- dbReadTable (5.8 ms / 12.9 ms)
- collect (7.4 ms / 14.4 ms)
- dplyr (14.9 ms / 27.6 ms)
cat("# dbSendQuery\n")
# dbSendQuery
f <- function(con){
rs <- dbSendQuery(con, "select * from mtcars")
dbFetch(rs)
dbClearResult(rs)
}
microbenchmark(
odbc = f(con1),
RPostgreSQL = f(con2),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 4.280109 4.424589 5.123516 4.52174 4.628829 59.40869 100
RPostgreSQL 10.708193 10.941653 12.106602 11.01144 11.139357 86.00633 100
cat("# dbGetQuery\n")
# dbGetQuery
microbenchmark(
odbc = dbGetQuery(con1, "select * from mtcars"),
RPostgreSQL = dbGetQuery(con2, "select * from mtcars"),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 4.369826 4.462516 4.639547 4.605441 4.78284 5.970596 100
RPostgreSQL 11.303488 11.523170 11.844457 11.642717 11.84386 21.660472 100
cat("# dbReadTable\n")
# dbReadTable
microbenchmark(
odbc = dbReadTable(con1, "mtcars"),
RPostgreSQL = dbReadTable(con2, "mtcars"),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 5.014894 5.224398 5.615451 5.402754 5.563106 16.63474 100
RPostgreSQL 11.410670 11.746018 12.680341 11.995234 12.305746 45.56513 100
cat("# collect\n")
# collect
t1 <- tbl(con1, "mtcars")
t2 <- tbl(con2, "mtcars")
microbenchmark(
odbc = collect(t1),
RPostgreSQL = collect(t2),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 6.823231 7.073533 7.937514 7.219613 7.46282 27.94842 100
RPostgreSQL 13.195611 13.699607 14.808574 13.937911 14.55319 71.37980 100
cat("# dplyr\n")
# dplyr
microbenchmark(
odbc = tbl(con1, "mtcars") %>% collect,
RPostgreSQL = tbl(con2, "mtcars") %>% collect,
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 13.38358 13.94573 14.34430 14.22169 14.56148 19.21916 100
RPostgreSQL 24.56986 25.83352 27.05007 26.66041 27.83492 47.06476 100
Select all
Median result (small table / large table):
- odbc (5.2 ms / 2.7 s)
- RPostgres (13.2 ms / 5.0 s)
cat("# Small table\n")
# Small table
microbenchmark(
odbc = dbGetQuery(con1, "select * from mtcars"),
RPostgreSQL = dbGetQuery(con2, "select * from mtcars"),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 4.321348 4.611888 4.858451 4.855945 5.062818 6.202066 100
RPostgreSQL 11.200706 12.134937 12.514874 12.486769 12.801218 16.967667 100
cat("# Large table\n")
# Large table
microbenchmark(
odbc = dbGetQuery(con1, "select * from flights"),
RPostgreSQL = dbGetQuery(con2, "select * from flights"),
times = 5
)
Unit: seconds
expr min lq mean median uq max neval
odbc 2.591428 2.633310 2.763152 2.636291 2.692626 3.262106 5
RPostgreSQL 4.637700 4.650428 4.713604 4.658108 4.718158 4.903626 5
Count records
Median result (small table / large table):
- odbc (4.6 ms / 31.0 ms)
- RPostgreSQL (2.3 ms / 28.8 ms)
cat("# Small table\n")
# Small table
microbenchmark(
odbc = dbGetQuery(con1, "select count(*) from mtcars"),
RPostgreSQL = dbGetQuery(con2, "select count(*) from mtcars"),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 3.869836 3.972087 4.277864 4.034261 4.095987 26.725616 100
RPostgreSQL 2.092483 2.139317 2.263315 2.186958 2.311471 4.324677 100
cat("# Large table\n")
# Large table
microbenchmark(
odbc = dbGetQuery(con1, "select count(*) from flights"),
RPostgreSQL = dbGetQuery(con2, "select count(*) from flights"),
times = 100
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 29.93849 30.42176 30.72725 30.72235 30.93322 33.8406 100
RPostgreSQL 28.07182 28.47232 29.79854 28.66594 28.86219 105.1817 100
Aggregate
Median result (month / origin & dest):
- odbc (70.0 ms / 127.1 ms)
- RPostgreSQL (68.4 ms / 126.0 ms)
cat("# By month\n")
# By month
microbenchmark(
odbc = dbGetQuery(con1, "select month, count(*) from flights group by month"),
RPostgreSQL = dbGetQuery(con2, "select month, count(*) from flights group by month"),
times = 25
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 68.27169 68.52957 69.49427 69.54900 70.02755 71.79220 25
RPostgreSQL 67.36743 67.98919 69.43012 68.41578 68.78742 92.34194 25
cat("# By origin & dest\n")
# By origin & dest
microbenchmark(
odbc = dbGetQuery(con1, "select origin, dest, count(*) from flights group by origin, dest"),
RPostgreSQL = dbGetQuery(con2, "select origin, dest, count(*) from flights group by origin, dest"),
times = 25
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 124.9043 125.8146 131.6079 126.9300 127.8341 190.1420 25
RPostgreSQL 124.2582 125.3030 126.0579 125.9069 126.5720 131.3172 25
Join and aggregate
Median result:
- odbc (154.7 ms)
- RPostgreSQL (153.9 ms)
cat("# Join and aggregate\n")
# Join and aggregate
microbenchmark(
odbc = dbGetQuery(con1, "select name, count(*) from flights, airports where origin = faa group by name"),
RPostgreSQL = dbGetQuery(con2, "select name, count(*) from flights, airports where origin = faa group by name"),
times = 25
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 151.2754 154.3244 156.4881 154.7501 155.4205 198.0783 25
RPostgreSQL 150.3930 151.8969 154.4057 153.2702 154.3652 177.8400 25
Write table
Median result (small / large):
- odbc (88.8 ms / 876.8 ms)
- RPostgreSQL (39.3 ms / 49.7 ms)
cat("# Small table\n")
# Small table
microbenchmark(
odbc = dbWriteTable(con1, "tmp1", mtcars, overwrite = TRUE),
RPostgreSQL = dbWriteTable(con2, "tmp2", mtcars, overwrite = TRUE),
times = 25
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 78.44993 80.11186 81.54664 80.57682 81.30703 99.96081 25
RPostgreSQL 32.84248 33.83805 34.73810 34.11803 35.70990 37.38110 25
cat("# Large table\n")
# Large table
tmpdat <- nycflights13::flights[1:1000,]
microbenchmark(
odbc = dbWriteTable(con1, "tmp3", tmpdat, overwrite = TRUE),
RPostgreSQL = dbWriteTable(con2, "tmp4", tmpdat, overwrite = TRUE),
times = 5
)
Unit: milliseconds
expr min lq mean median uq max neval
odbc 769.39862 776.23975 796.75253 794.52786 820.40447 823.1920 5
RPostgreSQL 45.63735 46.53974 47.50063 47.15208 47.90107 50.2729 5
Summary
There was no one decisive winner for all tests. The odbc R package along with the RStudio Professional Drivers was decisively faster than RPostgreSQL when collecting records. For all other operations, the differences in performance between the two packages were comparable.
- Select all (odbc faster)
- Count all records (RPostgreSQL faster)
- Aggregate (RPostgreSQL faster)
- Join and Aggregate (odbc faster)
- Write table (odbc faster)
LS0tCnRpdGxlOiAnUGVyZm9ybWFuY2UgY29tcGFyaXNvbjogb2RiYyB2cyBSUG9zdGdyZVNRTCcKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb25maWcpCmNvbmYgPC0gZ2V0KCkKYGBgCgpUaGlzIGFuYWx5c2lzIGNvbXBhcmVzIHBlcmZvcm1hbmNlIGJldHdlZW4gdGhlIG9kYmMgcGFja2FnZSBhbmQgdGhlIFJTdHVkaW8gUHJvZmVzc2lvbmFsIERyaXZlcnMgYWdhaW5zdCB0aGUgUlBvc3RncmVTUUwgcGFja2FnZS4gVGhlc2UgdGVzdHMgYXJlIGNvbXBhcmVkIG9uIHNtYWxsIGFuZCBsYXJnZSB0YWJsZXM6CgpgYGB7ciBlY2hvPUZBTFNFfQpkYXRhLmZyYW1lKAogIFRlc3QgPSBjKCJSZWFkIiwgIkNvdW50IiwgIkdyb3VwIiwgIkpvaW4iLCAiV3JpdGUiKSwKICBvZGJjID0gYygyNzAwLCAzMSwgMTI3LjEsIDE1NC44LCA4NzYuOCksCiAgUlBvc3RncmVTUUwgPSBjKDUxMDAsIDI4LjgsIDEyNi4wLCAxNTUuMCwgNDkuNyksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikgJT4lCiAgZ2F0aGVyKCJwYWNrYWdlIiwgIm1pbGxpc2Vjb25kcyIsIG9kYmMsIFJQb3N0Z3JlU1FMKSAlPiUKICBnZ3Bsb3QoYWVzKHJlb3JkZXIoVGVzdCwgbWlsbGlzZWNvbmRzLCBtZWFuKSwgbWlsbGlzZWNvbmRzLCBmaWxsPXBhY2thZ2UpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJQZXJmb3JtYW5jZSBDb21wYXJpc29uOiBvZGJjIHZzIFJQb3N0Z3JlU1FMIiwgIk1lZGlhbiB0aW1lIGluIGxvZzEwIG1pbGxpc2Vjb25kcyIpICsKICB4bGFiKCIiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iQmx1ZXMiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlcmUgYXJlIG1hbnkgd2F5cyB0byBjb2xsZWN0IGRhdGEgaW50byBSIGZyb20gYSBkYXRhYmFzZS4gVGhlc2UgbWV0aG9kcyB3ZXJlIGV4YW1pbmVkLCByYW5rZWQgaW4gb3JkZXIgZnJvbSBiZXN0IHBlcmZvcm1pbmcgdG8gbGVhc3Q6CgpgYGB7ciwgZWNobz1GQUxTRX0KZGF0YS5mcmFtZSgKICBGdW5jdGlvbiA9IGMoImRiU2VuZFF1ZXJ5IiwgImRiR2V0UXVlcnkiLCAiZGJSZWFkVGFibGUiLCAiZHBseXIgY29sbGVjdCBvbmx5IiwgImRwbHlyIiksCiAgTVMgPSBjKDEyLjAsIDEyLjUsIDEyLjksIDE0LjQsIDI3LjYpCikgJT4lCiAgZ2dwbG90KGFlcyhyZW9yZGVyKEZ1bmN0aW9uLCAtTVMpLCBNUykpICsKICBnZW9tX2JhcihzdGF0ID0gIklkZW50aXR5IiwgZmlsbCA9ICJzdGVlbGJsdWUiKSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJQZXJmb3JtYW5jZSBjb21wYXJpc29uOiBRdWVyeSBtZXRob2RzIiwgIk1lZGlhbiB0aW1lIGluIG1pbGxpc2Vjb25kcyIpICsKICB4bGFiKCIiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iQmx1ZXMiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMjIG9kYmMgY29ubmVjdGlvbgoKYGBge3J9CmNvbjEgPC0gZGJDb25uZWN0KG9kYmM6Om9kYmMoKSwKICBEcml2ZXIgPSBjb25mJGRyaXZlciwKICBEYXRhYmFzZSA9IGNvbmYkZGF0YWJhc2UsCiAgUG9ydCA9IGNvbmYkcG9ydCwKICBTZXJ2ZXIgPSBjb25mJHNlcnZlciwgCiAgVUlEID0gY29uZiRVSUQsIAogIFBXRCA9IGNvbmYkUFdECikKYGBgCgojIyMgUlBvc3RncmVTUUwgY29ubmVjdGlvbgoKYGBge3J9CmNvbjIgPC0gZGJDb25uZWN0KFJQb3N0Z3JlU1FMOjpQb3N0Z3JlU1FMKCksCiAgZGJuYW1lID0gY29uZiRkYXRhYmFzZSwKICBwb3J0ID0gY29uZiRwb3J0LAogIGhvc3QgPSBjb25mJHNlcnZlciwgCiAgdXNlciA9IGNvbmYkVUlELCAKICBwYXNzd29yZCA9IGNvbmYkUFdECikKYGBgCgojIyMgUXVlcnkgZnVuY3Rpb25zCgpNZWRpYW4gcmVzdWx0IChvZGJjIC8gUlBvc3RncmVTUUwpOgoKKiBkYlNlbmRRdWVyeSAoNC45IG1zIC8gMTIuMCBtcykKKiBkYkdldFF1ZXJ5ICg0LjkgbXMgLyAxMi41IG1zKQoqIGRiUmVhZFRhYmxlICg1LjggbXMgLyAxMi45IG1zKQoqIGNvbGxlY3QgKDcuNCBtcyAvIDE0LjQgbXMpCiogZHBseXIgKDE0LjkgbXMgLyAyNy42IG1zKQoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CmNhdCgiIyBkYlNlbmRRdWVyeVxuIikKZiA8LSBmdW5jdGlvbihjb24pewogIHJzIDwtIGRiU2VuZFF1ZXJ5KGNvbiwgInNlbGVjdCAqIGZyb20gbXRjYXJzIikKICBkYkZldGNoKHJzKQogIGRiQ2xlYXJSZXN1bHQocnMpCn0KbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGYoY29uMSksCiAgUlBvc3RncmVTUUwgPSBmKGNvbjIpLAogIHRpbWVzID0gMTAwCikKY2F0KCIjIGRiR2V0UXVlcnlcbiIpCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSBkYkdldFF1ZXJ5KGNvbjEsICJzZWxlY3QgKiBmcm9tIG10Y2FycyIpLAogIFJQb3N0Z3JlU1FMID0gZGJHZXRRdWVyeShjb24yLCAic2VsZWN0ICogZnJvbSBtdGNhcnMiKSwKICB0aW1lcyA9IDEwMAopCmNhdCgiIyBkYlJlYWRUYWJsZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiUmVhZFRhYmxlKGNvbjEsICJtdGNhcnMiKSwKICBSUG9zdGdyZVNRTCA9IGRiUmVhZFRhYmxlKGNvbjIsICJtdGNhcnMiKSwKICB0aW1lcyA9IDEwMAopCmNhdCgiIyBjb2xsZWN0XG4iKQp0MSA8LSB0YmwoY29uMSwgIm10Y2FycyIpCnQyIDwtIHRibChjb24yLCAibXRjYXJzIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGNvbGxlY3QodDEpLAogIFJQb3N0Z3JlU1FMID0gY29sbGVjdCh0MiksCiAgdGltZXMgPSAxMDAKKQpjYXQoIiMgZHBseXJcbiIpCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSB0YmwoY29uMSwgIm10Y2FycyIpICU+JSBjb2xsZWN0LAogIFJQb3N0Z3JlU1FMID0gdGJsKGNvbjIsICJtdGNhcnMiKSAlPiUgY29sbGVjdCwKICB0aW1lcyA9IDEwMAopCmBgYAoKIyMjIFNlbGVjdCBhbGwKCk1lZGlhbiByZXN1bHQgKHNtYWxsIHRhYmxlIC8gbGFyZ2UgdGFibGUpOgoKKiBvZGJjICg1LjIgbXMgLyAyLjcgcykKKiBSUG9zdGdyZXMgKDEzLjIgbXMgLyA1LjAgcykKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQpjYXQoIiMgU21hbGwgdGFibGVcbiIpCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSBkYkdldFF1ZXJ5KGNvbjEsICJzZWxlY3QgKiBmcm9tIG10Y2FycyIpLAogIFJQb3N0Z3JlU1FMID0gZGJHZXRRdWVyeShjb24yLCAic2VsZWN0ICogZnJvbSBtdGNhcnMiKSwKICB0aW1lcyA9IDEwMAopCmNhdCgiIyBMYXJnZSB0YWJsZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCAqIGZyb20gZmxpZ2h0cyIpLAogIFJQb3N0Z3JlU1FMID0gZGJHZXRRdWVyeShjb24yLCAic2VsZWN0ICogZnJvbSBmbGlnaHRzIiksCiAgdGltZXMgPSA1CikKYGBgCgojIyMgQ291bnQgcmVjb3JkcwoKTWVkaWFuIHJlc3VsdCAoc21hbGwgdGFibGUgLyBsYXJnZSB0YWJsZSk6CgoqIG9kYmMgKDQuNiBtcyAvIDMxLjAgbXMpCiogUlBvc3RncmVTUUwgKDIuMyAgbXMgLyAyOC44IG1zKQoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CmNhdCgiIyBTbWFsbCB0YWJsZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCBjb3VudCgqKSBmcm9tIG10Y2FycyIpLAogIFJQb3N0Z3JlU1FMID0gZGJHZXRRdWVyeShjb24yLCAic2VsZWN0IGNvdW50KCopIGZyb20gbXRjYXJzIiksCiAgdGltZXMgPSAxMDAKKQpjYXQoIiMgTGFyZ2UgdGFibGVcbiIpCm1pY3JvYmVuY2htYXJrKAogIG9kYmMgPSBkYkdldFF1ZXJ5KGNvbjEsICJzZWxlY3QgY291bnQoKikgZnJvbSBmbGlnaHRzIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgY291bnQoKikgZnJvbSBmbGlnaHRzIiksCiAgdGltZXMgPSAxMDAKKQpgYGAKCiMjIyBBZ2dyZWdhdGUKCk1lZGlhbiByZXN1bHQgKG1vbnRoIC8gb3JpZ2luICYgZGVzdCk6CgoqIG9kYmMgKDcwLjAgbXMgLyAxMjcuMSBtcykKKiBSUG9zdGdyZVNRTCAoNjguNCBtcyAvIDEyNi4wIG1zKQoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CmNhdCgiIyBCeSBtb250aFxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCBtb250aCwgY291bnQoKikgZnJvbSBmbGlnaHRzIGdyb3VwIGJ5IG1vbnRoIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgbW9udGgsIGNvdW50KCopIGZyb20gZmxpZ2h0cyBncm91cCBieSBtb250aCIpLAogIHRpbWVzID0gMjUKKQpjYXQoIiMgQnkgb3JpZ2luICYgZGVzdFxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCBvcmlnaW4sIGRlc3QsIGNvdW50KCopIGZyb20gZmxpZ2h0cyBncm91cCBieSBvcmlnaW4sIGRlc3QiKSwKICBSUG9zdGdyZVNRTCA9IGRiR2V0UXVlcnkoY29uMiwgInNlbGVjdCBvcmlnaW4sIGRlc3QsIGNvdW50KCopIGZyb20gZmxpZ2h0cyBncm91cCBieSBvcmlnaW4sIGRlc3QiKSwKICB0aW1lcyA9IDI1CikKYGBgCgojIyMgSm9pbiBhbmQgYWdncmVnYXRlCgpNZWRpYW4gcmVzdWx0OgoKKiBvZGJjICgxNTQuNyBtcykKKiBSUG9zdGdyZVNRTCAoMTUzLjkgbXMpCgpgYGB7ciwgY29sbGFwc2U9VFJVRX0KY2F0KCIjIEpvaW4gYW5kIGFnZ3JlZ2F0ZVxuIikKbWljcm9iZW5jaG1hcmsoCiAgb2RiYyA9IGRiR2V0UXVlcnkoY29uMSwgInNlbGVjdCBuYW1lLCBjb3VudCgqKSBmcm9tIGZsaWdodHMsIGFpcnBvcnRzIHdoZXJlIG9yaWdpbiA9IGZhYSBncm91cCBieSBuYW1lIiksCiAgUlBvc3RncmVTUUwgPSBkYkdldFF1ZXJ5KGNvbjIsICJzZWxlY3QgbmFtZSwgY291bnQoKikgZnJvbSBmbGlnaHRzLCBhaXJwb3J0cyB3aGVyZSBvcmlnaW4gPSBmYWEgZ3JvdXAgYnkgbmFtZSIpLAogIHRpbWVzID0gMjUKKQpgYGAKCiMjIyBXcml0ZSB0YWJsZQoKTWVkaWFuIHJlc3VsdCAoc21hbGwgLyBsYXJnZSk6CgoqIG9kYmMgKDg4LjggbXMgLyA4NzYuOCBtcykKKiBSUG9zdGdyZVNRTCAoMzkuMyBtcyAvIDQ5LjcgbXMpCgpgYGB7ciwgY29sbGFwc2U9VFJVRX0KY2F0KCIjIFNtYWxsIHRhYmxlXG4iKQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJXcml0ZVRhYmxlKGNvbjEsICJ0bXAxIiwgbXRjYXJzLCBvdmVyd3JpdGUgPSBUUlVFKSwKICBSUG9zdGdyZVNRTCA9IGRiV3JpdGVUYWJsZShjb24yLCAidG1wMiIsIG10Y2Fycywgb3ZlcndyaXRlID0gVFJVRSksCiAgdGltZXMgPSAyNQopCmNhdCgiIyBMYXJnZSB0YWJsZVxuIikKdG1wZGF0IDwtIG55Y2ZsaWdodHMxMzo6ZmxpZ2h0c1sxOjEwMDAsXQptaWNyb2JlbmNobWFyaygKICBvZGJjID0gZGJXcml0ZVRhYmxlKGNvbjEsICJ0bXAzIiwgdG1wZGF0LCBvdmVyd3JpdGUgPSBUUlVFKSwKICBSUG9zdGdyZVNRTCA9IGRiV3JpdGVUYWJsZShjb24yLCAidG1wNCIsIHRtcGRhdCwgb3ZlcndyaXRlID0gVFJVRSksCiAgdGltZXMgPSA1CikKYGBgCgojIyMgU3VtbWFyeQoKVGhlcmUgd2FzIG5vIG9uZSBkZWNpc2l2ZSB3aW5uZXIgZm9yIGFsbCB0ZXN0cy4gVGhlIG9kYmMgUiBwYWNrYWdlIGFsb25nIHdpdGggdGhlIFJTdHVkaW8gUHJvZmVzc2lvbmFsIERyaXZlcnMgd2FzIGRlY2lzaXZlbHkgZmFzdGVyIHRoYW4gUlBvc3RncmVTUUwgd2hlbiBjb2xsZWN0aW5nIHJlY29yZHMuIEZvciBhbGwgb3RoZXIgb3BlcmF0aW9ucywgdGhlIGRpZmZlcmVuY2VzIGluIHBlcmZvcm1hbmNlIGJldHdlZW4gdGhlIHR3byBwYWNrYWdlcyB3ZXJlIGNvbXBhcmFibGUuCgoqIFNlbGVjdCBhbGwgKG9kYmMgZmFzdGVyKQoqIENvdW50IGFsbCByZWNvcmRzIChSUG9zdGdyZVNRTCBmYXN0ZXIpCiogQWdncmVnYXRlIChSUG9zdGdyZVNRTCBmYXN0ZXIpCiogSm9pbiBhbmQgQWdncmVnYXRlIChvZGJjIGZhc3RlcikKKiBXcml0ZSB0YWJsZSAob2RiYyBmYXN0ZXIpCg==