Ниже у меня есть три функции, которые выполняют одну и ту же операцию - дублируют данный data.frame и rbind его себе (т.е. плохая практика выращивания объекта).
Первая функция, f1, копирует входной объект в новый объект, x, затем увеличивает этот объект и, наконец, заменяет входной объект.
Вторая функция, f2, копирует входной объект в новый объект, x, а затем увеличивает входной объект.
Третья функция, f3, только увеличивает входной объект.
Я ожидал, что f1 будет самым медленным, учитывая, что он по существу требует изменения распределения памяти как для df, так и для x. Напротив, все 3 функции кажутся примерно равными по времени вычислений. Как я могу понять такое поведение? Мой пример ошибочен?
## Functions
# copy df, grow copy, replace df with copy
f1 <- function(df){
x <- df
x <- rbind(x, x)
df <- x
return(df)
}
# copy df, grow df
f2 <- function(df){
x <- df
df <- rbind(df, x)
return(df)
}
# grow df
f3 <- function(df){
df <- rbind(df, df)
return(df)
}
## Benchmark
df <- rbind(iris)
res <- microbenchmark(f1(df), f2(df), f3(df), times=5000L)
## Print results:
print(res)
# Unit: microseconds
# expr min lq mean median uq max neval
# f1(df) 255.66 263.591 292.6851 270.123 292.516 2693.291 5000
# f2(df) 255.66 263.591 302.5159 270.590 292.516 15460.876 5000
# f3(df) 255.66 263.591 299.6157 270.122 292.516 3613.758 5000
## Plot results:
boxplot(res)
@LAP - Да, я думал об этом, но даже в этом случае должен быть вопрос о выращивании объектов.
@ chinsoon12 - Я не вижу разницы даже при использовании df <- matrix(runif (1e6), 1e3, 1e3) (times=100L).
Думаю, комментарий @LAP актуален. Вы вызываете функцию не рекурсивно, а каждую функцию N раз.
Но вы выращиваете объект во всех трех функциях? Я не вижу разницы в работе. R не волнует, какой объект вы выращиваете.





Функции R используют копирование при изменении.
Итак, когда вы передаете df в качестве аргумента, если вы не изменяете его, он будет указывать на тот же объект, который вы передали (тот же адрес).
То же самое происходит, если вы назначаете один и тот же объект.
Например, используя address <- function(x) cat(data.table::address(x), "\n"):
> x <- 1
> address(x)
0x58164b8
> y <- x
> address(y)
0x58164b8
Итак, теперь напечатайте несколько адресов в ваших функциях
## Functions
# copy df, grow copy, replace df with copy
f1 <- function(df){
address(x <- df)
address(x <- rbind(x, x))
address(df <- x)
return(df)
}
# copy df, grow df
f2 <- function(df){
address(x <- df)
address(df <- rbind(df, x))
return(df)
}
Результат:
> df <- rbind(iris)
> address(df)
0x543e378
> res1 <- f1(df)
0x543e378
0x5d3bd08
0x5d3bd08
> res2 <- f2(df)
0x543e378
0x5c89e40
Итак, каждая из ваших функций создает только один новый объект, поэтому они имеют одинаковый размер.
Учитывая, что функции R оцениваются во временной среде, выделение памяти было бы новым для всех трех функций, не так ли?