首页 > 解决方案 > 为什么以下函数的性能不会因增加一个额外的对象而受到惩罚?

问题描述

下面我有三个执行相同操作的函数 - 复制给定的 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)

在此处输入图像描述

标签: rperformancememory-management

解决方案


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 

因此,您的每个函数都只创建一个新对象,这就是它们具有相同足迹的原因。


推荐阅读