|
| 1 | +#!/usr/bin/env Rscript |
| 2 | +# Benchmark discord discovery — primarily HOT-SAX (Phase 2 z-norm precompute). |
| 3 | +# |
| 4 | +# Usage: |
| 5 | +# Rscript bench_discords.R [label] [out_csv] |
| 6 | +# |
| 7 | +# Example before/after on poptiplex: |
| 8 | +# git checkout 37353dc && R CMD INSTALL --library=$HOME/R/library . |
| 9 | +# Rscript inst/site/bench_discords.R pre-phase2 |
| 10 | +# git checkout master && R CMD INSTALL --library=$HOME/R/library . |
| 11 | +# Rscript inst/site/bench_discords.R post-phase2 |
| 12 | +# Rscript inst/site/bench_discords.R --compare bench_discords_results.csv |
| 13 | + |
| 14 | +suppressPackageStartupMessages(library(jmotif)) |
| 15 | + |
| 16 | +args <- commandArgs(trailingOnly = TRUE) |
| 17 | + |
| 18 | +if (length(args) >= 1 && args[[1]] == "--compare") { |
| 19 | + csv <- if (length(args) >= 2) args[[2]] else "bench_discords_results.csv" |
| 20 | + if (!file.exists(csv)) stop("CSV not found: ", csv) |
| 21 | + d <- read.csv(csv, stringsAsFactors = FALSE) |
| 22 | + agg <- aggregate( |
| 23 | + elapsed_sec ~ label + benchmark, |
| 24 | + data = d, |
| 25 | + FUN = function(x) c(mean = mean(x), sd = sd(x), min = min(x), max = max(x)) |
| 26 | + ) |
| 27 | + flat <- data.frame( |
| 28 | + label = agg$label, |
| 29 | + benchmark = agg$benchmark, |
| 30 | + mean_sec = agg$elapsed_sec[, "mean"], |
| 31 | + sd_sec = agg$elapsed_sec[, "sd"], |
| 32 | + min_sec = agg$elapsed_sec[, "min"], |
| 33 | + max_sec = agg$elapsed_sec[, "max"], |
| 34 | + stringsAsFactors = FALSE |
| 35 | + ) |
| 36 | + labels <- unique(flat$label) |
| 37 | + if (length(labels) >= 2) { |
| 38 | + wide <- reshape(flat[, c("label", "benchmark", "mean_sec")], |
| 39 | + idvar = "benchmark", timevar = "label", direction = "wide") |
| 40 | + pre <- grep("pre", labels, value = TRUE)[1] |
| 41 | + post <- grep("post", labels, value = TRUE)[1] |
| 42 | + if (is.na(pre)) pre <- labels[1] |
| 43 | + if (is.na(post)) post <- labels[length(labels)] |
| 44 | + pre_col <- paste0("mean_sec.", pre) |
| 45 | + post_col <- paste0("mean_sec.", post) |
| 46 | + if (pre_col %in% names(wide) && post_col %in% names(wide)) { |
| 47 | + wide$speedup <- wide[[pre_col]] / wide[[post_col]] |
| 48 | + cat("\n=== Speedup (", pre, " / ", post, " = times faster) ===\n", sep = "") |
| 49 | + print(wide[, c("benchmark", pre_col, post_col, "speedup")], row.names = FALSE) |
| 50 | + } |
| 51 | + } |
| 52 | + cat("\n=== Summary by label ===\n") |
| 53 | + print(flat, row.names = FALSE) |
| 54 | + quit(save = "no", status = 0) |
| 55 | +} |
| 56 | + |
| 57 | +label <- if (length(args) >= 1) args[[1]] else format(Sys.time(), "%Y%m%d-%H%M") |
| 58 | +out_csv <- if (length(args) >= 2) args[[2]] else "bench_discords_results.csv" |
| 59 | + |
| 60 | +benchmark_one <- function(name, expr, n = 5, warmup = 2) { |
| 61 | + for (i in seq_len(warmup)) eval(expr, envir = parent.frame()) |
| 62 | + gc(verbose = FALSE) |
| 63 | + |
| 64 | + times <- numeric(n) |
| 65 | + for (i in seq_len(n)) { |
| 66 | + gc(verbose = FALSE) |
| 67 | + t0 <- proc.time() |
| 68 | + eval(expr, envir = parent.frame()) |
| 69 | + times[i] <- (proc.time() - t0)[["elapsed"]] |
| 70 | + } |
| 71 | + |
| 72 | + data.frame( |
| 73 | + label = label, |
| 74 | + benchmark = name, |
| 75 | + n = n, |
| 76 | + elapsed_sec = times, |
| 77 | + timestamp = format(Sys.time(), "%Y-%m-%d %H:%M:%S"), |
| 78 | + stringsAsFactors = FALSE |
| 79 | + ) |
| 80 | +} |
| 81 | + |
| 82 | +x <- ecg0606 |
| 83 | +x_medium <- rep(ecg0606, 3L) |
| 84 | + |
| 85 | +cat("=== jmotif discord benchmarks ===\n") |
| 86 | +cat("label:", label, "\n") |
| 87 | +cat("package:", as.character(packageVersion("jmotif")), "\n") |
| 88 | +cat("ecg0606 length:", length(x), "\n") |
| 89 | +cat("medium series length:", length(x_medium), "\n\n") |
| 90 | + |
| 91 | +cases <- list( |
| 92 | + list(name = "hotsax_ecg0606_4disc", |
| 93 | + expr = quote(find_discords_hotsax(x, 100, 4, 4, 0.01, 4)), |
| 94 | + n = 10, warmup = 3), |
| 95 | + list(name = "hotsax_ecg0606_5disc", |
| 96 | + expr = quote(find_discords_hotsax(x, 100, 4, 4, 0.01, 5)), |
| 97 | + n = 10, warmup = 3), |
| 98 | + list(name = "hotsax_medium_4disc", |
| 99 | + expr = quote(find_discords_hotsax(x_medium, 100, 4, 4, 0.01, 4)), |
| 100 | + n = 5, warmup = 2), |
| 101 | + list(name = "brute_ecg0606_4disc", |
| 102 | + expr = quote(find_discords_brute_force(x, 100, 4, 0.01)), |
| 103 | + n = 5, warmup = 2), |
| 104 | + list(name = "wordbag_ecg0606", |
| 105 | + expr = quote(series_to_wordbag(x, 60, 6, 6, "exact", 0.01)), |
| 106 | + n = 10, warmup = 3), |
| 107 | + list(name = "rra_ecg0606_4disc", |
| 108 | + expr = quote(find_discords_rra(x, 100, 4, 4, "none", 0.01, 4)), |
| 109 | + n = 5, warmup = 2), |
| 110 | + list(name = "sax_medium_w160", |
| 111 | + expr = quote(sax_via_window(x_medium, 160, 4, 4, "none", 0.01)), |
| 112 | + n = 5, warmup = 2) |
| 113 | +) |
| 114 | + |
| 115 | +results <- NULL |
| 116 | +for (case in cases) { |
| 117 | + cat("Running", case$name, "...\n") |
| 118 | + flush.console() |
| 119 | + chunk <- benchmark_one(case$name, case$expr, n = case$n, warmup = case$warmup) |
| 120 | + print(aggregate(elapsed_sec ~ 1, data = chunk, FUN = mean)) |
| 121 | + results <- if (is.null(results)) chunk else rbind(results, chunk) |
| 122 | +} |
| 123 | + |
| 124 | +agg <- aggregate(elapsed_sec ~ benchmark, data = results, FUN = function(v) { |
| 125 | + c(mean = mean(v), sd = sd(v), min = min(v), max = max(v)) |
| 126 | +}) |
| 127 | +summary_df <- data.frame( |
| 128 | + benchmark = agg$benchmark, |
| 129 | + mean_sec = round(agg$elapsed_sec[, "mean"], 4), |
| 130 | + sd_sec = round(agg$elapsed_sec[, "sd"], 4), |
| 131 | + min_sec = round(agg$elapsed_sec[, "min"], 4), |
| 132 | + max_sec = round(agg$elapsed_sec[, "max"], 4) |
| 133 | +) |
| 134 | + |
| 135 | +cat("=== Run summary (label =", label, ") ===\n") |
| 136 | +print(summary_df, row.names = FALSE) |
| 137 | + |
| 138 | +if (file.exists(out_csv)) { |
| 139 | + existing <- read.csv(out_csv, stringsAsFactors = FALSE) |
| 140 | + combined <- rbind(existing, results) |
| 141 | +} else { |
| 142 | + combined <- results |
| 143 | +} |
| 144 | +write.csv(combined, out_csv, row.names = FALSE) |
| 145 | +cat("\nAppended to", out_csv, "(", nrow(combined), "rows )\n") |
0 commit comments