Можно ли с помощью base-R ускорить код в ~ 1000 раз?

Воспроизводимый пример ниже можно использовать в качестве тестового примера. Я ищу решение base-R, потому что

  • У меня нет опыта работы с C++ (для интеграции Rcpp) или Java (для интеграции rJava)
  • Таким образом, возможно, что "проблема скорости" заложена в алгоритме.
  • Мне нравятся инструменты отчетности и преимущества быстрого прототипирования R.

Фон согласно вводу @Gregor:

Первоначальная проблема связана с проблемами набора номера. Таким образом, возникает вопрос «а лучше ли объединить два тура?» миллиарды раз. В приведенном ниже примере такая же структура (На каком расстоянии находится квартира с двумя местами? В каком месте лучше всего остановиться? Каков результат, если мы объединим две экскурсии?).
Мы знаем о замечательном ответ как ускорить R-код, но в нашем случае у нас есть дополнительная «бизнес-логика», которая проявляется в различных операторах if-else. Таким образом, нам кажется невозможным получить векторизованный код при наличии операторов if-else - но, возможно, мы что-то упускаем.

Воспроизводимый пример

generate_random_sequence <- function(nrows=1000) {
  x <- c(0, runif (nrows, min = 0, max = 100), 0)
  y <- c(0, runif (nrows, min = 0, max = 100), 0)
  loc <- c("Start", floor(runif (nrows, 1000, 9999)), "Start")
  return(data.table::data.table(x = x, y = y, loc = loc, stringsAsFactors = FALSE))
}

dist <- function(x1, y1, x2, y2) {
  return(sqrt((x1-x2)^2 + (y1-y2)^2))
}

get_best_loc <- function(cur_loc, stop1, stop2) {
  d1 <- dist(cur_loc$x, cur_loc$y, stop1$x, stop1$y)
  d2 <- dist(cur_loc$x, cur_loc$y, stop2$x, stop2$y)
  if (d1 <= d2) {
    best_stop <- stop1
    stop_id <- 1L
  } else {
    best_stop <- stop2
    stop_id <- 2L
  }
  return(list(best_stop = best_stop, stop_id = stop_id))
}

combine_sequence <- function(t1, t2) {
  t_combined <- c(1, rep(NA, nrow(t1) + nrow(t2) - 4), 1)
  ind_max <- c(nrow(t1) - 1, nrow(t2) - 1)
  last_stop <- t1[1, ]
  ind_next_stop <- c(2, 2)
  ind_t_combined <- 1
  while (all(ind_next_stop <= ind_max)) {
    r <- get_best_loc(last_stop, 
                        t1[ind_next_stop[1],], t2[ind_next_stop[2],])
    best_stop <- r$best_stop; stop_id <- r$stop_id; rm(r)
    if (stop_id == 1) {
      ind_next_stop[1] <- ind_next_stop[1] + 1
    } else {
      ind_next_stop[2] <- ind_next_stop[2] + 1
    }
    ind_t_combined <- ind_t_combined + 1
    t_combined[ind_t_combined] <- stop_id
    last_stop <- best_stop
  }

  if (ind_next_stop[1] < ind_max[1]) {
    t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <- 
      t1$loc[ind_next_stop[1]:(nrow(t1) - 1)]
  } else {
    t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <- 
      t2$loc[ind_next_stop[2]:(nrow(t2) - 1)]
  }
  return(t_combined)
}

n <- 1e2
t1 <- generate_random_sequence(n)
t2 <- generate_random_sequence(n)
microbenchmark::microbenchmark(combine_sequence(t1, t2),
                               times = 10L, unit = "s")

Ввод из комментариев:

Когда я использую профилирование, я получаю следующий результат. Мне непонятно, есть ли у меня рычаги, где я могу получить ускорение в 1000 раз. (Я бы просто был в темноте)

Можно ли с помощью base-R ускорить код в ~ 1000 раз?

Код для профилирования:

  profvis::profvis({
    t_combined <- c(1, rep(NA, nrow(t1) + nrow(t2) - 4), 1)
    ind_max <- c(nrow(t1) - 1, nrow(t2) - 1)
    last_stop <- t1[1, ]
    ind_next_stop <- c(2, 2)
    ind_t_combined <- 1
    while (all(ind_next_stop <= ind_max)) {
      r <- get_best_loc(last_stop, 
                        t1[ind_next_stop[1],], t2[ind_next_stop[2],])
      best_stop <- r$best_stop; stop_id <- r$stop_id; rm(r)
      if (stop_id == 1) {
        ind_next_stop[1] <- ind_next_stop[1] + 1
      } else {
        ind_next_stop[2] <- ind_next_stop[2] + 1
      }
      ind_t_combined <- ind_t_combined + 1
      t_combined[ind_t_combined] <- stop_id
      last_stop <- best_stop
    }

    if (ind_next_stop[1] < ind_max[1]) {
      t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <- 
        t1$loc[ind_next_stop[1]:(nrow(t1) - 1)]
    } else {
      t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <- 
        t2$loc[ind_next_stop[2]:(nrow(t2) - 1)]
    }
})
Глянь сюда для ресурсов по профилированию кода R.
Gregor Thomas 07.08.2018 18:05

Кроме того, как только вы определили узкие места в производительности, подумайте об использовании Rcpp или rJava для ускорения этих частей без необходимости переносить всю кодовую базу.

Gregor Thomas 07.08.2018 18:11

Одна из потенциальных проблем - rbind, ср. stackoverflow.com/questions/50990237/….

Ralf Stubner 07.08.2018 18:18

Выглядит лучше. Я снова открою, но, пожалуйста, также опишите свой код словами. Комментариев нет, поэтому было бы неплохо иметь описание цели. Ваше профилирование показывает, что на get_best_loc тратится много времени. Но, глядя на код, мне совсем не ясно, какова цель combine_sequence или почему вам нужен цикл while, выполняемый по очереди, а не, скажем, группировать каждую 1000 вместе для некоторой векторизации.

Gregor Thomas 09.08.2018 17:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
108
0

Другие вопросы по теме