Почему вычисления быстрее с al oop? - PullRequest
0 голосов
/ 25 января 2020

Обновление:

Как указал @ Dave2e, перемещение оператора start_time (в коде 2) из ​​for l oop сделает время выполнения сравнимо с кодом 1. А именно:

start_time <- Sys.time()
for (j in 1:1) {
   n <- 100
   result <- 1
   for (i in 1:n) {
     result <- result * i
   }
   result
   ## [1] 9.332622e+157
   end_time <- Sys.time()
}
end_time - start_time

Действительно ли for l oop действительно улучшает производительность или это подделка?


Оригинальное сообщение:

У меня есть две части кода следующим образом:

Код 1:

start_time <- Sys.time()
n <- 100
result <- 1
for (i in 1:n) {
   result <- result * i
}
result
## [1] 9.332622e+157
end_time <- Sys.time()
end_time - start_time

Код 2:

for (j in 1:1) {
   start_time <- Sys.time()
   n <- 100
   result <- 1
   for (i in 1:n){
      result <- result * i}
      result
      ## [1] 9.332622e+157
      end_time <- Sys.time()
   }
   end_time - start_time

Я ожидал, что эти два кода работают одинаково, но код 2 постоянно работает значительно быстрее, чем код 1. На моем компьютере код 1 занимает около 10 ^ -2 секунд, а код 2 - около 5 * 10 ^ -6 секунд. Любое понимание того, как это могло произойти? Если простое добавление for l oop ко всем кодам может сократить время работы программы, я буду использовать его для всех своих кодов в будущем.

1 Ответ

3 голосов
/ 25 января 2020

Я не думаю, что ваше сравнение очень надежное. Очень сложно что-либо сказать об относительной синхронизации очень быстрого кода, не запуская его много раз, чтобы получить среднее значение - слишком много неконтролируемых факторов могут немного изменить время выполнения.

Вывод, который я могу сделать из приведенных ниже тестов заключается в том, что инкапсуляция довольно тривиального вычисления в избыточном для l oop не сильно ранит , но любое очевидное преимущество является тривиальным и, вероятно, просто эффектом шума.

I инкапсулировал каждый из ваших кусков кода в функцию (with_loop и without_loop), поместив function() { ... } вокруг каждого из них. (Обратите внимание, что это означает, что я не основываю время на ваших Sys.time() сравнениях, а на встроенном времени в пакете microbenchmark.)

Пакет microbenchmark больше подходит для бенчмаркинга особенно для очень коротких вычислительных задач: из ?microbenchmark::microbenchmark:

'microbenchmark' служит более точной заменой часто встречающегося выражения system.time (replicate (1000, expr)) '. Он изо всех сил старается точно измерить только время, необходимое для оценки «expr». Для достижения этого используются функции точной синхронизации, составляющие менее миллисекунды (предположительно наносекунды), которые предоставляют большинство современных операционных систем. Кроме того, все вычисления выражений выполняются в коде C, чтобы минимизировать любые издержки.

library(microbenchmark)
m1 <- microbenchmark(with_loop, without_loop)
library(ggplot2)
autoplot(m1)+scale_y_log10()

Квантили (lq, медиана, uq) практически идентичны.

Unit: nanoseconds
         expr min lq   mean median uq   max neval cld
    with_loop  36 38  48.56     39 40   972   100   a
 without_loop  36 39 177.81     40 41 13363   100   a

enter image description here

Код без l oop действительно медленнее в среднем (то есть имеет большее среднее значение ), но это почти полностью обусловлено парой выбросов.

Теперь сосредоточимся только на значениях менее 50 наносекунд:

autoplot(m1)+scale_y_log10(limits=c(NA,50))

enter image description here

Если мы сделаем это снова с times=1e6 (миллион итераций), мы получим почти идентичные результаты: среднее значение с l oop будет на 3 наносекунд быстрее (опять же, вероятно, почти полностью обусловлено небольшие колебания в верхнем хвосте).

Unit: nanoseconds
         expr min lq     mean median uq     max neval cld
    with_loop  32 39 86.44248     41 61 2474675 1e+06   a
 without_loop  35 39 89.86294     41 61 2915836 1e+06   a

Если вам нужно запустить l oop миллиард раз, это будет соответствовать 3-секундной разнице во времени выполнения. Наверное, не стоит беспокоиться.

...