diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a9474e78e38c6722e4a6b86eaedc66d477ee9ecc..e8136201fe87f7c98de9009e8e22b993cf60d585 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,6 +23,10 @@ default:
   stage: check
   script:
   - R -e 'rcmdcheck::rcmdcheck(args = ifelse(as.logical(Sys.getenv("NOT_CRAN")), "", "--as-cran"), error_on = "warning")'
+  - R -e 'install.packages("airGR", repos = "http://cran.r-project.org")'
+  - Rscript tests/scheduled_tests/benchmarkRunModel.R
+  - R CMD INSTALL .
+  - Rscript tests/scheduled_tests/benchmarkRunModel.R
 
 .check_not_cran:
   variables:
diff --git a/tests/scheduled_tests/benchmarkRunmodel.R b/tests/scheduled_tests/benchmarkRunmodel.R
new file mode 100644
index 0000000000000000000000000000000000000000..1ff3e100854751a531e37654fd4e927c8d511a7b
--- /dev/null
+++ b/tests/scheduled_tests/benchmarkRunmodel.R
@@ -0,0 +1,49 @@
+library(airGR)
+source("tests/testthat/helper_scheduled_Calibration.R")
+
+BenchmarkRunModel <- function(model) {
+
+  e <- PrepareCalibration(model)
+  for(n in ls(e, all.names=TRUE)) assign(n, get(n, e))
+
+  # RunOptions calibration configuration
+  RunOptions$Outputs_Sim <- RunOptions$Outputs_Cal
+  mbm <- microbenchmark::microbenchmark(RunModel =
+    RunModel(InputsModel = InputsModel, RunOptions = RunOptions,
+             Param = as.numeric(strsplit(model$ParamFinalR, ";")[[1]]), FUN_MOD = sModel)
+  )
+  mbm$time
+}
+
+Args <- commandArgs(trailingOnly = TRUE)
+sModelNames <- paste0(dfModels$name,
+                     ifelse(as.logical(dfModels$IsHyst), "_Hyst", ""))
+
+dfBM <- as.data.frame(apply(dfModels, 1, BenchmarkRunModel))
+colnames(dfBM) <- sModelNames
+dfBM <- cbind(version = as.character(packageVersion('airGR')), dfBM)
+
+
+file <- "tests/tmp/benchmark.txt"
+write.table(dfBM, file = file,
+            row.names = FALSE, col.names = !file.exists(file), quote = FALSE,
+            sep = "\t", append = TRUE)
+
+df <- read.table(file = file, sep = "\t", header = TRUE)
+if(length(unique(df$version)) > 1) {
+
+  lV <- lapply(unique(df$version), function(version) {
+    apply(df[df$version == version, -1] / 1E6, 2, mean)
+  })
+  names(lV) <- unique(df$version)
+  dfMean <- cbind(model = sModelNames, as.data.frame(t(do.call(rbind, lV))))
+  dfMean$evolution <- (dfMean[,3] - dfMean[,2]) / dfMean[,2]
+  write.table(dfMean, "tests/tmp/mean_execution_time.txt", row.names = FALSE, quote = F, sep = "\t")
+  res <- testthat::test_file("tests/testthat/benchmark_RunModel.R")
+  dRes <- as.data.frame(res)
+  if (any(dRes[, "failed"] > 0) | any(dRes[, "error"])) {
+    quit(status = 1)
+  }
+}
+
+
diff --git a/tests/scheduled_tests/regression.R b/tests/scheduled_tests/regression.R
index 88c7b0f6511f99953bb07a56d497735388b53d37..79fd1a9a42f48e3edbd21ad33bab94b4bdbef684 100644
--- a/tests/scheduled_tests/regression.R
+++ b/tests/scheduled_tests/regression.R
@@ -34,13 +34,15 @@ StoreExampleResults <- function(package, path, run.dontrun = FALSE, run.donttest
   # Get the list of documentation pages
   rd <- unique(readRDS(system.file("help", "aliases.rds", package = package)))
 
-  dir.create(path, showWarnings = FALSE)
+  unlink(path, recursive = TRUE)
+  dir.create(path)
 
   lapply(
     rd,
     StoreTopicResults,
     package, path, run.dontrun = run.dontrun, run.donttest = run.donttest
   )
+
 }
 
 StoreTopicResults <- function(topic, package, path, run.dontrun = TRUE, run.donttest = TRUE) {
@@ -54,12 +56,21 @@ StoreTopicResults <- function(topic, package, path, run.dontrun = TRUE, run.dont
   varBefore <- c()
   varBefore <- ls(envir = globalenv())
 
+  start_time = Sys.time()
+
   example(
     topic, package = package, character.only = TRUE, echo = FALSE, ask = FALSE, local = FALSE, setRNG = TRUE,
     run.dontrun = run.dontrun, run.donttest = run.donttest
   )
+
+  end_time = Sys.time()
   dev.off()
 
+  write.table(data.frame(topic = topic, time = end_time - start_time),
+            file.path(path, "timing.csv"),
+            row.names = FALSE, col.names = FALSE, quote = FALSE,
+            sep = "\t", append = TRUE)
+
   varAfter <- ls(envir = globalenv())
 
   varToSave <- setdiff(varAfter, varBefore)
diff --git a/tests/testthat/benchmarkRunModel.R b/tests/testthat/benchmarkRunModel.R
new file mode 100644
index 0000000000000000000000000000000000000000..aff3a83856a242bc2bae336fde22fd8e21bac1a5
--- /dev/null
+++ b/tests/testthat/benchmarkRunModel.R
@@ -0,0 +1,7 @@
+df <- read.table("../tmp/mean_execution_time.txt", sep = "\t", header = T)
+
+lapply(df$model, function(model) {
+  test_that(paste(model, ": RunModel should be as fast as CRAN version"), {
+    expect_lt(df$evolution[df$model == model], 1.1)
+  })
+})
diff --git a/tests/testthat/helper_scheduled_Calibration.R b/tests/testthat/helper_scheduled_Calibration.R
new file mode 100644
index 0000000000000000000000000000000000000000..8933332dcc4e114cac3baa2188f2730a32f2c906
--- /dev/null
+++ b/tests/testthat/helper_scheduled_Calibration.R
@@ -0,0 +1,134 @@
+# Calibrations on v1.6.12
+sModelCalibrations <- c(
+  "name          IsHyst data     aggreg ParamFinalR",
+  "GR1A          FALSE  L0123001 %Y     0.91125",
+  "GR2M          FALSE  L0123001 %Y%m   259.8228;0.9975",
+  "GR4J          FALSE  L0123001 NA     223.6315877;0.5781516;97.5143942;2.2177177",
+  "GR5J          FALSE  L0123001 NA     220.3863609;0.8944531;93.5640705;1.7628720;0.4846427",
+  "GR6J          FALSE  L0123001 NA     192.8761657;0.6933087;49.1783293;2.2145422;0.5088240;6.8146261",
+  "CemaNeigeGR4J FALSE  L0123001 NA     2.043839e+02;5.781516e-01;1.025141e+02;2.217718e+00;1.501502e-03;1.432036e+01",
+  "CemaNeigeGR5J FALSE  L0123001 NA     1.983434e+02;8.747758e-01;9.849443e+01;1.768769e+00;4.829830e-01;1.501502e-03;1.432036e+01",
+  "CemaNeigeGR6J FALSE  L0123001 NA     184.9341841;0.5551637;59.7398917;2.2177177;0.4760000;6.0496475;0.0000000;14.4642868",
+  "CemaNeigeGR4J TRUE   L0123001 NA     208.5127103;0.5781516;102.5140641;2.2274775;0.0000000;6.7644613;8.0000000;1.0000000",
+  "CemaNeigeGR5J TRUE   L0123001 NA     202.350228;0.901525;98.494430;1.788288;0.483984;0.000000;7.401500;6.100000;1.000000",
+  "CemaNeigeGR6J TRUE   L0123001 NA     188.67010241;0.56662930;60.34028760;2.22747748;0.47600000;5.98945247;0.03203203;7.93816892;10.80000000;1.00000000",
+  "GR4H            FALSE  L0123003 NA     711.676649;-1.158469;150.556095;4.686093",
+  "GR5H            FALSE  L0123003 NA     804.0021672;-0.1898488;137.7524699;3.0436628;0.1951163",
+  "CemaNeigeGR4H   FALSE  L0123003 NA     1.595685e+03;-8.183484e-01;2.320697e+02;5.000000e-01;5.005005e-04;9.342369e+01",
+  "CemaNeigeGR5H   FALSE  L0123003 NA     33.34921883;-4.98925432;332.00673122;1.58534106;0.20792716;0.02214393;4.28498513",
+  "CemaNeigeGR4H   TRUE   L0123003 NA     1.766316e+03;-6.920667e-01;2.192034e+02;3.451688e+00;5.005005e-04;4.869585e+01;1.111447e+01;5.064090e-01",
+  "CemaNeigeGR5H   TRUE   L0123003 NA     66.6863310;-1.4558128;138.3795123;2.6499450;0.2325000;0.0000000;0.3017014;48.4000000;0.9914915"
+)
+
+dfModels <- read.table(text = paste(sModelCalibrations, collapse = "\n"), header = TRUE)
+
+PrepareCalibration <- function(model) {
+  model <- as.list(model)
+  sModel <- paste0("RunModel_", model$name)
+  sIM_FUN_MOD <- sModel
+
+  if (model$data == "L0123003") {
+    # hourly time step database
+    dates <- c("2004-01-01 00:00", "2004-12-31 23:00", "2005-01-01 00:00", "2008-12-31 23:00")
+    date_format = "%Y-%m-%d %H:%M"
+    TempMean <- fakeHourlyTemp()
+  } else {
+    # yearly, monthly, daily time step databases
+    dates <- c("1985-01-01", "1985-12-31", "1986-01-01", "2012-12-31")
+    date_format <- "%Y-%m-%d"
+    if (!is.na(model$aggreg)) {
+      # Aggregation on monthly and yearly databases
+      sIM_FUN_MOD <- "RunModel_GR4J" # CreateInputsModel with daily data
+      date_format <- model$aggreg
+    }
+  }
+
+  ## loading catchment data
+  data(list = model$data)
+  if (model$data != "L0123003") TempMean <- BasinObs$T
+
+  # preparation of the InputsModel object
+  InputsModel <- CreateInputsModel(FUN_MOD = sIM_FUN_MOD,
+                                   DatesR = BasinObs$DatesR,
+                                   Precip = BasinObs$P,
+                                   PotEvap = BasinObs$E,
+                                   TempMean = TempMean,
+                                   ZInputs = median(BasinInfo$HypsoData),
+                                   HypsoData = BasinInfo$HypsoData,
+                                   NLayers = 5)
+
+  if (!is.na(model$aggreg)) {
+    # conversion of InputsModel to target time step
+    InputsModel <- SeriesAggreg(InputsModel, Format = model$aggreg)
+
+    dfQobs <- SeriesAggreg(data.frame(DatesR = BasinObs$DatesR, Qmm = BasinObs$Qmm),
+                           Format = model$aggreg, ConvertFun = "sum")
+    Obs <- dfQobs$Qmm
+  } else {
+    Obs <- BasinObs$Qmm
+  }
+
+  # calibration period selection
+  dates <- sapply(dates, function(x) format(as.Date(x), format = date_format))
+  Ind_WarmUp <- seq(
+    which(format(InputsModel$DatesR, format = date_format)==dates[1]),
+    which(format(InputsModel$DatesR, format = date_format)==dates[2])
+  )
+  Ind_Run <- seq(
+    which(format(InputsModel$DatesR, format = date_format)==dates[3]),
+    which(format(InputsModel$DatesR, format = date_format)==dates[4])
+  )
+
+  # preparation of the RunOptions object
+  suppressWarnings(
+    RunOptions <- CreateRunOptions(
+      FUN_MOD = sModel,
+      InputsModel = InputsModel,
+      IndPeriod_Run = Ind_Run,
+      IndPeriod_WarmUp = Ind_WarmUp,
+      IsHyst = as.logical(model$IsHyst)
+    )
+  )
+
+  # calibration criterion: preparation of the InputsCrit object
+  InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_NSE, InputsModel = InputsModel,
+                                 RunOptions = RunOptions, Obs = Obs[Ind_Run])
+  # preparation of CalibOptions object
+  CalibOptions <- CreateCalibOptions(sModel, IsHyst = as.logical(model$IsHyst))
+
+  return(environment())
+
+}
+
+ModelCalibration <- function(model) {
+
+  e <- PrepareCalibration(model)
+  for(n in ls(e, all.names=TRUE)) assign(n, get(n, e))
+
+  # calibration
+  suppressWarnings(OutputsCalib <- Calibration(InputsModel = InputsModel, RunOptions = RunOptions,
+                                               InputsCrit = InputsCrit, CalibOptions = CalibOptions,
+                                               FUN_MOD = sModel))
+  OutputsCalib$ParamFinalR
+}
+
+#' Create Fake hourly temperature from daily temperatures in L0123001
+#'
+#' @param start_date [character] start date in format "%Y-%m-%d"
+#' @param end_date [character] end date in format "%Y-%m-%d"
+#' @return [numeric] hourly temperature time series between `start_date` and `end_date`
+fakeHourlyTemp <- function(start_date = "2004-01-01", end_date = "2008-12-31") {
+  dates <- as.POSIXct(c(start_date, end_date), tz = "UTC")
+  data(L0123002)
+  indJ <- seq.int(which(BasinObs$DatesR == as.POSIXct(dates[1])),
+                  which(BasinObs$DatesR == as.POSIXct(dates[2])))
+  TJ <- BasinObs$T[indJ]
+
+  TH <- approx((seq.int(length(TJ)) - 1) * 24,TJ,
+               seq.int(length(TJ) * 24 ) - 1,
+               rule = 2)$y
+  varT_1J <- -sin(0:23/24 * 2 * pi) # Temp min at 6 and max at 18
+  varT <- rep(varT_1J, length(TJ))
+  TH <- TH + varT * 5 # For a mean daily amplitude of 10°
+  TH
+}
diff --git a/tests/testthat/scheduled-Calibration.R b/tests/testthat/scheduled-Calibration.R
index 6b6b98b6a0426913d2b54c49283d2339c9b44f42..0951e0b97d91bbe3c4a62e90906d670d385a3cb3 100644
--- a/tests/testthat/scheduled-Calibration.R
+++ b/tests/testthat/scheduled-Calibration.R
@@ -1,106 +1,7 @@
 context("Calibration")
 
-sModels <- c(
-  "name          IsHyst data     aggreg ParamFinalR",
-  "GR1A          FALSE  L0123001 %Y     0.91125",
-  "GR2M          FALSE  L0123001 %Y%m   259.8228;0.9975",
-  "GR4J          FALSE  L0123001 NA     223.6315877;0.5781516;97.5143942;2.2177177",
-  "GR5J          FALSE  L0123001 NA     220.3863609;0.8944531;93.5640705;1.7628720;0.4846427",
-  "GR6J          FALSE  L0123001 NA     192.8761657;0.6933087;49.1783293;2.2145422;0.5088240;6.8146261",
-  "CemaNeigeGR4J FALSE  L0123001 NA     2.043839e+02;5.781516e-01;1.025141e+02;2.217718e+00;1.501502e-03;1.432036e+01",
-  "CemaNeigeGR5J FALSE  L0123001 NA     1.983434e+02;8.747758e-01;9.849443e+01;1.768769e+00;4.829830e-01;1.501502e-03;1.432036e+01",
-  "CemaNeigeGR6J FALSE  L0123001 NA     184.9341841;0.5551637;59.7398917;2.2177177;0.4760000;6.0496475;0.0000000;14.4642868",
-  "CemaNeigeGR4J TRUE   L0123001 NA     208.5127103;0.5781516;102.5140641;2.2274775;0.0000000;6.7644613;8.0000000;1.0000000",
-  "CemaNeigeGR5J TRUE   L0123001 NA     202.350228;0.901525;98.494430;1.788288;0.483984;0.000000;7.401500;6.100000;1.000000",
-  "CemaNeigeGR6J TRUE   L0123001 NA     188.67010241;0.56662930;60.34028760;2.22747748;0.47600000;5.98945247;0.03203203;7.93816892;10.80000000;1.00000000",
-  "GR4H            FALSE  L0123003 NA     711.676649;-1.158469;150.556095;4.686093",
-  "GR5H            FALSE  L0123003 NA     804.0021672;-0.1898488;137.7524699;3.0436628;0.1951163",
-  "CemaNeigeGR4H   FALSE  L0123003 NA     1.595685e+03;-8.183484e-01;2.320697e+02;5.000000e-01;5.005005e-04;9.342369e+01",
-  "CemaNeigeGR5H   FALSE  L0123003 NA     33.34921883;-4.98925432;332.00673122;1.58534106;0.20792716;0.02214393;4.28498513",
-  "CemaNeigeGR4H   TRUE   L0123003 NA     1.766316e+03;-6.920667e-01;2.192034e+02;3.451688e+00;5.005005e-04;4.869585e+01;1.111447e+01;5.064090e-01",
-  "CemaNeigeGR5H   TRUE   L0123003 NA     66.6863310;-1.4558128;138.3795123;2.6499450;0.2325000;0.0000000;0.3017014;48.4000000;0.9914915"
-)
-dfModels <- read.table(text = paste(sModels, collapse = "\n"), header = TRUE)
+# helpers functions and dfModels variable are in tests/testthat/helper_scheduled_Calibration.R
 
-ModelCalibration <- function(model) {
-  sModel <- paste0("RunModel_", model$name)
-  sIM_FUN_MOD <- sModel
-
-  if (model$data == "L0123003") {
-    # hourly time step database
-    dates <- c("2004-01-01 00:00", "2004-12-31 23:00", "2005-01-01 00:00", "2008-12-31 23:00")
-    date_format = "%Y-%m-%d %H:%M"
-    TempMean <- fakeHourlyTemp()
-  } else {
-    # yearly, monthly, daily time step databases
-    dates <- c("1985-01-01", "1985-12-31", "1986-01-01", "2012-12-31")
-    date_format <- "%Y-%m-%d"
-    if (!is.na(model$aggreg)) {
-      # Aggregation on monthly and yearly databases
-      sIM_FUN_MOD <- "RunModel_GR4J" # CreateInputsModel with daily data
-      date_format <- model$aggreg
-    }
-  }
-
-  ## loading catchment data
-  data(list = model$data)
-  if (model$data != "L0123003") TempMean <- BasinObs$T
-
-  # preparation of the InputsModel object
-  InputsModel <- CreateInputsModel(FUN_MOD = sIM_FUN_MOD,
-                                   DatesR = BasinObs$DatesR,
-                                   Precip = BasinObs$P,
-                                   PotEvap = BasinObs$E,
-                                   TempMean = TempMean,
-                                   ZInputs = median(BasinInfo$HypsoData),
-                                   HypsoData = BasinInfo$HypsoData,
-                                   NLayers = 5)
-
-  if (!is.na(model$aggreg)) {
-    # conversion of InputsModel to target time step
-    InputsModel <- SeriesAggreg(InputsModel, Format = model$aggreg)
-
-    dfQobs <- SeriesAggreg(data.frame(DatesR = BasinObs$DatesR, Qmm = BasinObs$Qmm),
-                           Format = model$aggreg, ConvertFun = "sum")
-    Obs <- dfQobs$Qmm
-  } else {
-    Obs <- BasinObs$Qmm
-  }
-
-  # calibration period selection
-  dates <- sapply(dates, function(x) format(as.Date(x), format = date_format))
-  Ind_WarmUp <- seq(
-    which(format(InputsModel$DatesR, format = date_format)==dates[1]),
-    which(format(InputsModel$DatesR, format = date_format)==dates[2])
-  )
-  Ind_Run <- seq(
-    which(format(InputsModel$DatesR, format = date_format)==dates[3]),
-    which(format(InputsModel$DatesR, format = date_format)==dates[4])
-  )
-
-  # preparation of the RunOptions object
-  suppressWarnings(
-    RunOptions <- CreateRunOptions(
-      FUN_MOD = sModel,
-      InputsModel = InputsModel,
-      IndPeriod_Run = Ind_Run,
-      IndPeriod_WarmUp = Ind_WarmUp,
-      IsHyst = as.logical(model$IsHyst)
-    )
-  )
-
-  # calibration criterion: preparation of the InputsCrit object
-  InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_NSE, InputsModel = InputsModel,
-                                 RunOptions = RunOptions, Obs = Obs[Ind_Run])
-  # preparation of CalibOptions object
-  CalibOptions <- CreateCalibOptions(sModel, IsHyst = as.logical(model$IsHyst))
-
-  # calibration
-  suppressWarnings(OutputsCalib <- Calibration(InputsModel = InputsModel, RunOptions = RunOptions,
-                                               InputsCrit = InputsCrit, CalibOptions = CalibOptions,
-                                               FUN_MOD = sModel))
-  OutputsCalib$ParamFinalR
-}
 
 
 TestModelCalibration <- function(model) {
@@ -116,26 +17,4 @@ TestModelCalibration <- function(model) {
   })
 }
 
-
-#' Create Fake hourly temperature from daily temperatures in L0123001
-#'
-#' @param start_date [character] start date in format "%Y-%m-%d"
-#' @param end_date [character] end date in format "%Y-%m-%d"
-#' @return [numeric] hourly temperature time series between `start_date` and `end_date`
-fakeHourlyTemp <- function(start_date = "2004-01-01", end_date = "2008-12-31") {
-  dates <- as.POSIXct(c(start_date, end_date), tz = "UTC")
-  data(L0123002)
-  indJ <- seq.int(which(BasinObs$DatesR == as.POSIXct(dates[1])),
-                  which(BasinObs$DatesR == as.POSIXct(dates[2])))
-  TJ <- BasinObs$T[indJ]
-
-  TH <- approx((seq.int(length(TJ)) - 1) * 24,TJ,
-               seq.int(length(TJ) * 24 ) - 1,
-               rule = 2)$y
-  varT_1J <- -sin(0:23/24 * 2 * pi) # Temp min at 6 and max at 18
-  varT <- rep(varT_1J, length(TJ))
-  TH <- TH + varT * 5 # For a mean daily amplitude of 10°
-  TH
-}
-
 apply(dfModels, 1, TestModelCalibration)