This is made for 1920x1080 screens minimum!

Server Setup:

  • Windows Server 2012 R2 Datacenter, without Meltdown / Spectre patch
  • Intel Core i7-7700HQ, 3.8 GHz singlethread, 3.4 GHz turbo boost all cores + NVIDIA 1050 Ti, 1.75 GHz
  • 32GB RAM 2400 MHz DDR4
  • 3GBps read SSD
  • R 3.4.3, Visual Studio 2015, MinGW 4.9
  • xgboost: commit 98be9ae (Jan 28, 2018, 2:06 AM GMT+1)

1 Setup

1.1 CSS Injection

cat("
<style>
body .main-container {
    max-width: 100% !important;
}
body .main-container/*, .toc-content*/ {
  padding-left: 5px !important;
  padding-right: 5px !important;
}
@media (min-width: 1919px) {
  .col-md-3 {
    width: 15% !important;
  }
  .col-md-9 {
    width: 85% !important;
  }
  .toc-content {
    padding-left: 10px !important;
    padding-right: 10px !important;
  }
}
.dygraph-label, .dygraph-title, .dygraph-xlabel, .dygraph-ylabel, .dygraph-y2label, .dygraph-axis-label, .dygraph-axis-label-x, .dygraph-axis-label-y, .dygraph-axis-label-y2 {
  font-family: \"Roboto Condensed\"; !important
}
</style>
")

1.2 Load Libraries

library(extrafont)
## Registering fonts with R
library(ggplot2)
library(data.table)
library(dygraphs)

1.3 Load Data

xgb_gpu_roam <- fread("xgb_bench_gpu_fast_142.csv")[, c("Threads", "Time", "Efficiency", "Normalized")]
xgb_gpu_pin <- fread("xgb_bench_gpu_fast_142_pin.csv")[, c("Threads", "Time", "Efficiency", "Normalized")]

1.4 Settings

cpu_name <- "Core i7-7700HQ"
n_sockets <- c(1, 4, 2)
n_cores <- 8
freq_table <- c(3.8, 3.6, rep(3.4, 6))
core_priority <- c(1 + 2 * (0:3), 2 + 2 * (0:3))

element_alpha <- 0.5
text_size <- 8
core_size <- 14
text_vjust <- -0.35
core_vjust <- 2
base_size <- 27
y_mult <- 1.05

1.5 Plotting Functions

make_plot_compare_per_thread <- function(input1, input2, titles, name1, name2, lr, bt) {
  
  temp_data <- merge(input1[, c("Threads", "Time")], input2[, c("Threads", "Time")], by = "Threads", suffixes = c(paste0(" (", name1, ")"), paste0(" (", name2, ")")))
  dygraph(temp_data, main = paste0(titles, " Timings (seconds)"), xlab = "Threads", ylab = "Time (s)", width = 1000, height = 500) %>%
    dyAxis("x", valueFormatter = htmlwidgets::JS(paste0("function(d) {return(\"<b><span style=\'color:red\'>\" + d + \" CPU (\" + (", n_cores / n_sockets[1] / 2, " - (-Math.min(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0)) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ")+ \" threads, \" + (Math.max(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ") + \" hyperthreads)</span></b>\")}"))) %>%
    dyAxis("y", valueFormatter = htmlwidgets::JS(paste0("function(d) {return(Math.round(d * 10) / 10 + \" seconds (+\" + Math.round((Math.round((d - ", round(min(input1$Time, input2$Time), digits = 2), ") * 1000) / 1000) * 10) / 10 + \"%)\")}"))) %>%
    dySeries(paste0("Time (", name1, ")"), label = paste0("Time (", name1, ")"), color = "#008000") %>%
    dySeries(paste0("Time (", name2, ")"), label = paste0("Time (", name2, ")"), color = "#000080") %>%
    dyRangeSelector(fillColor = "#ffccff", strokeColor = "#cc00cc") %>% 
    dyUnzoom() %>% 
    dyCrosshair(direction = "both") %>%
    dyRoller(rollPeriod = 1, showRoller = TRUE) %>%
    dyLegend(show = "always", hideOnMouseOut = FALSE, width = 350, showZeroValues = TRUE, labelsSeparateLines = TRUE) %>%
    dyRibbon(data = as.numeric(input1$Time > input2$Time) + as.numeric(input1$Time >= input2$Time), palette = c("#ccffcc", "#999999", "#ccccff"), top = 0.01, bottom = 0) %>%
    dyShading(from = min(input1$Time), to = min(input2$Time), axis = "y") %>%
    dyShading(from = max(input1$Time), to = max(input2$Time), axis = "y") %>%
    dyLimit(min(input1$Time), label = paste0(name1, " Best (", sprintf("%02.03f", min(input1$Time)), " seconds)"), labelLoc = lr, color = "#008000") %>%
    dyLimit(min(input2$Time), label = paste0(name2, " Best (", sprintf("%02.03f", min(input2$Time)), " seconds)"), labelLoc = lr, color = "#000080") %>%
    dyEvent(which.min(input1$Time), label = paste0(name1, " Best (", which.min(input1$Time), " threads)             "), labelLoc = bt, color = "#008000") %>%
    dyEvent(which.min(input2$Time), label = paste0(name2, " Best (", which.min(input2$Time), " threads)             "), labelLoc = bt, color = "#000080") %>%
    dyOptions(axisLineWidth = 5,
              drawPoints = TRUE,
              drawAxesAtZero = TRUE,
              includeZero = TRUE,
              logscale = FALSE,
              pointSize = 3,
              fillGraph = FALSE,
              axisLineColor = "navy", 
              gridLineColor = "lightblue") %>%
    dyHighlight(highlightCircleSize = 5, 
                highlightSeriesBackgroundAlpha = 0.5,
                highlightSeriesOpts = list(strokeWidth = 3),
                hideOnMouseOut = TRUE)
  
}

make_plot_compare_per_efficiency <- function(input1, input2, titles, name1, name2, lr, bt) {
  
  temp_data <- merge(input1[, c("Threads", "Time")], input2[, c("Threads", "Time")], by = "Threads", suffixes = c(paste0(" (", name1, ")"), paste0(" (", name2, ")")))
  temp_data[[paste0("Efficiency (", name1, ")")]] <- (1 / temp_data[[paste0("Time (", name1, ")")]]) / (1 / (freq_table[1]/min(freq_table) * max(temp_data[[paste0("Time (", name1, ")")]][1], temp_data[[paste0("Time (", name2, ")")]][1])))
  temp_data[[paste0("Efficiency (", name2, ")")]] <- (1 / temp_data[[paste0("Time (", name2, ")")]]) / (1 / (freq_table[1]/min(freq_table) * max(temp_data[[paste0("Time (", name1, ")")]][1], temp_data[[paste0("Time (", name2, ")")]][1])))
  temp_data <- temp_data[, c(1, 4, 5)]
  dygraph(temp_data, main = paste0(titles, " Efficiency vs 1 Thread @", sprintf("%.01f", min(freq_table)), "GHz"), xlab = "Threads", ylab = "Efficiency (100%)", width = 1000, height = 500) %>%
    dyAxis("x", valueFormatter = htmlwidgets::JS(paste0("function(d) {return(\"<b><span style=\'color:red\'>\" + d + \" CPU (\" + (", n_cores / n_sockets[1] / 2, " - (-Math.min(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0)) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ")+ \" threads, \" + (Math.max(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ") + \" hyperthreads)</span></b>\")}"))) %>%
    dyAxis("y", valueRange = c(0, max(c(temp_data[[2]], temp_data[[3]])) * 1.30), valueFormatter = htmlwidgets::JS(paste0("function(d) {return(Math.round(d * 1000) / 10 + \"% Efficiency\")}"))) %>%
    dySeries(paste0("Efficiency (", name1, ")"), label = paste0("Efficiency (", name1, ")"), color = "#008000") %>%
    dySeries(paste0("Efficiency (", name2, ")"), label = paste0("Efficiency (", name2, ")"), color = "#000080") %>%
    dyRangeSelector(fillColor = "#ffccff", strokeColor = "#cc00cc") %>% 
    dyUnzoom() %>% 
    dyCrosshair(direction = "both") %>%
    dyRoller(rollPeriod = 1, showRoller = TRUE) %>%
    dyLegend(show = "always", hideOnMouseOut = FALSE, width = 350, showZeroValues = TRUE, labelsSeparateLines = TRUE) %>%
    dyRibbon(data = as.numeric(temp_data[[2]] < temp_data[[3]]) + as.numeric(temp_data[[2]] <= temp_data[[3]]), palette = c("#ccffcc", "#999999", "#ccccff"), top = 0.88, bottom = 0.75) %>%
    dyShading(from = min(min(temp_data[[2]]), min(temp_data[[3]])), to = max(min(temp_data[[2]]), min(temp_data[[3]])), axis = "y") %>%
    dyShading(from = min(max(temp_data[[2]]), max(temp_data[[3]])), to = max(max(temp_data[[2]]), max(temp_data[[3]])), axis = "y") %>%
    dyLimit(temp_data[[2]][which.min(input1$Time)], label = paste0(name1, " Best (", sprintf("%02.01f", 100 * temp_data[[2]][which.min(input1$Time)]), "% Efficiency)"), labelLoc = lr, color = "#008000") %>%
    dyLimit(temp_data[[3]][which.min(input2$Time)], label = paste0(name2, " Best (", sprintf("%02.01f", 100 * temp_data[[3]][which.min(input2$Time)]), "% Efficiency)"), labelLoc = lr, color = "#000080") %>%
    dyEvent(which.min(input1$Time), label = paste0("Roam Best (", which.min(input1$Time), " threads)                   "), labelLoc = bt, color = "#008000") %>%
    dyEvent(which.min(input2$Time), label = paste0("Pin Best (", which.min(input2$Time), " threads)                   "), labelLoc = bt, color = "#000080") %>%
    dyOptions(axisLineWidth = 5,
              drawPoints = TRUE,
              drawAxesAtZero = TRUE,
              includeZero = TRUE,
              logscale = FALSE,
              pointSize = 3,
              fillGraph = FALSE,
              axisLineColor = "navy", 
              gridLineColor = "lightblue") %>%
    dyHighlight(highlightCircleSize = 5, 
                highlightSeriesBackgroundAlpha = 0.5,
                highlightSeriesOpts = list(strokeWidth = 3),
                hideOnMouseOut = TRUE)
  
}

make_plot_compare_per_normalized <- function(input1, input2, titles, name1, name2, lr, bt) {
  
  temp_data <- merge(input1[, c("Threads", "Time")], input2[, c("Threads", "Time")], by = "Threads", suffixes = c(paste0(" (", name1, ")"), paste0(" (", name2, ")")))
  temp_data[[paste0("Normalized (", name1, ")")]] <- (1 / temp_data[[paste0("Time (", name1, ")")]]) / (1 / (freq_table[1]/min(freq_table) * max(temp_data[[paste0("Time (", name1, ")")]][1], temp_data[[paste0("Time (", name2, ")")]][1]))) / 1:n_cores
  temp_data[[paste0("Normalized (", name2, ")")]] <- (1 / temp_data[[paste0("Time (", name2, ")")]]) / (1 / (freq_table[1]/min(freq_table) * max(temp_data[[paste0("Time (", name1, ")")]][1], temp_data[[paste0("Time (", name2, ")")]][1]))) / 1:n_cores
  temp_data <- temp_data[, c(1, 4, 5)]
  dygraph(temp_data, main = paste0(titles, " Normalized vs 1 Thread @", sprintf("%.01f", min(freq_table)), "GHz"), xlab = "Threads", ylab = "Normalized Efficiency (100%)", width = 1000, height = 500) %>%
    dyAxis("x", valueFormatter = htmlwidgets::JS(paste0("function(d) {return(\"<b><span style=\'color:red\'>\" + d + \" CPU (\" + (", n_cores / n_sockets[1] / 2, " - (-Math.min(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0)) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ")+ \" threads, \" + (Math.max(d - ", n_cores / n_sockets[1], " * (Math.floor((d - 1) / ", n_cores / n_sockets[1], ")) - ", n_cores / n_sockets[1] / 2, ", 0) + Math.floor((d - 1) / ", n_cores / n_sockets[1], ") * ", n_cores / n_sockets[1] / 2, ") + \" hyperthreads)</span></b>\")}"))) %>%
    dyAxis("y", valueRange = c(0, max(c(input1$Normalized, input2$Normalized)) * 1.30), valueFormatter = htmlwidgets::JS(paste0("function(d) {return(Math.round(d * 1000) / 10 + \"% Normalized\")}"))) %>%
    dySeries(paste0("Normalized (", name1, ")"), label = paste0("Normalized (", name1, ")"), color = "#008000") %>%
    dySeries(paste0("Normalized (", name2, ")"), label = paste0("Normalized (", name2, ")"), color = "#000080") %>%
    dyRangeSelector(fillColor = "#ffccff", strokeColor = "#cc00cc") %>% 
    dyUnzoom() %>% 
    dyCrosshair(direction = "both") %>%
    dyRoller(rollPeriod = 1, showRoller = TRUE) %>%
    dyLegend(show = "always", hideOnMouseOut = FALSE, width = 350, showZeroValues = TRUE, labelsSeparateLines = TRUE) %>%
    dyRibbon(data = as.numeric(temp_data[[2]] < temp_data[[3]]) + as.numeric(temp_data[[2]] <= temp_data[[3]]), palette = c("#ccffcc", "#999999", "#ccccff"), top = 0.88, bottom = 0.75) %>%
    dyLimit(temp_data[[2]][which.min(input1$Time)], label = paste0(name1, " Best (", sprintf("%02.01f", 100 * temp_data[[2]][which.min(input1$Time)]), "% Normalized)"), labelLoc = lr, color = "#008000") %>%
    dyLimit(temp_data[[3]][which.min(input2$Time)], label = paste0(name2, " Best (", sprintf("%02.01f", 100 * temp_data[[3]][which.min(input2$Time)]), "% Normalized)"), labelLoc = lr, color = "#000080") %>%
    dyEvent(which.min(input1$Time), label = paste0("Roam Best (", which.min(input1$Time), " threads)                   "), labelLoc = bt, color = "#008000") %>%
    dyEvent(which.min(input2$Time), label = paste0("Pin Best (", which.min(input2$Time), " threads)                   "), labelLoc = bt, color = "#000080") %>%
    dyOptions(axisLineWidth = 5,
              drawPoints = TRUE,
              drawAxesAtZero = TRUE,
              includeZero = TRUE,
              logscale = FALSE,
              pointSize = 3,
              fillGraph = FALSE,
              axisLineColor = "navy", 
              gridLineColor = "lightblue") %>%
    dyHighlight(highlightCircleSize = 5, 
                highlightSeriesBackgroundAlpha = 0.5,
                highlightSeriesOpts = list(strokeWidth = 3),
                hideOnMouseOut = TRUE)
  
}

2 Plot Compare Roaming and Pinned CPUs on GPU

2.1 xgboost

2.1.1 xgboost, Visual Studio 2017

2.1.1.1 xgboost Thread Performance (VS 2017)

make_plot_compare_per_thread(xgb_gpu_roam, xgb_gpu_pin, "[Roaming vs Pinned CPU, VS 2017] GPU xgboost", "Roam", "Pin", "right", "top")

2.1.1.2 xgboost Efficiency Performance (VS 2017)

make_plot_compare_per_efficiency(xgb_gpu_roam, xgb_gpu_pin, "[Roaming vs Pinned CPU, VS 2017] GPU xgboost", "Roam", "Pin", "right", "bottom")

2.1.1.3 xgboost Normalized Performance (VS 2017)

make_plot_compare_per_normalized(xgb_gpu_roam, xgb_gpu_pin, "[Roaming vs Pinned CPU, VS 2017] GPU xgboost", "Roam", "Pin", "right", "top")