3

I recently was on a bug hunt and made an observation that you can reproduce with the following lines of code:

library(lobstr)
lapply(1:10, function(g){
 record <- list(EK = rep(1,1))
 lobstr::obj_addr(record$EK)
})

lapply(1:10, function(g){
 record <- list(EK = 1)
 lobstr::obj_addr(record$EK)
})

Essentially, I am creating a variable called record which contains a list with a single element. In the one case I am setting this variable (EK) to be 1 and in the other I am doing the same but using rep(1,1).

When looking at the memory location (using the lobstr package) I find that in the case of setting EK = 1, all record variables have the same memory location, whereas those set with rep(1,1) have each their own memory location. This is the code with the output:

> lapply(1:10, function(g){
+  record <- list(EK = rep(1,1))
+  lobstr::obj_addr(record$EK)
+ })
[[1]]
[1] "0x7f9ff4bc3c20"

[[2]]
[1] "0x7f9ff4c8e668"

[[3]]
[1] "0x7f9ff4c8e438"

[[4]]
[1] "0x7f9ff4c8e208"

[[5]]
[1] "0x7f9ff4c8dfd8"

[[6]]
[1] "0x7f9ff4c8dda8"

[[7]]
[1] "0x7f9ff4c8db78"

[[8]]
[1] "0x7f9ff4c8d948"

[[9]]
[1] "0x7f9ff4c8d718"

[[10]]
[1] "0x7f9ff4c8d4e8"

> 
> lapply(1:10, function(g){
+  record <- list(EK = 1)
+  lobstr::obj_addr(record$EK)
+ })
[[1]]
[1] "0x7f9ff4ca3190"

[[2]]
[1] "0x7f9ff4ca3190"

[[3]]
[1] "0x7f9ff4ca3190"

[[4]]
[1] "0x7f9ff4ca3190"

[[5]]
[1] "0x7f9ff4ca3190"

[[6]]
[1] "0x7f9ff4ca3190"

[[7]]
[1] "0x7f9ff4ca3190"

[[8]]
[1] "0x7f9ff4ca3190"

[[9]]
[1] "0x7f9ff4ca3190"

[[10]]
[1] "0x7f9ff4ca3190"

The actual bug in my code (receiving the same output on repeated runs of the same simulation code in an mclapply() call) turned out not be caused by this (I thought it was because R used the same memory location, if that is actually the case) but by how the random number generator was initialised.

Still, I am curious. From the help file of ?obj_addr() I understand that "the address of an object is different every time you create it". So, why then does the one method create different memory addresses and the other doesn't?

Marco Smolla
  • 188
  • 5
  • 3
    Hmm … why wouldn’t it be? Even just seeing the initial code this was the behaviour I’d intuitively expect: a literal always has the same address, and each newly created vector has a distinct address. – Konrad Rudolph May 17 '22 at 09:39
  • 1
    Have a look at: [What exactly is copy-on-modify semantics in R, and where is the canonical source?](https://stackoverflow.com/q/15759117/10488504) – GKi May 17 '22 at 09:58

1 Answers1

2

Thanks Konrad, I did not know that. I assumed that every item get's its own place somewhere in memory. But I see now, if I change what is stored in each iteration of lapply(), the memory address will be different in either case:

library(lobstr)
lapply(1:10, function(g){
 record <- list(EK = rep(1,1))
 record$EK<-round(runif(1,0,1)*10)
 lobstr::obj_addr(record$EK)
})

lapply(1:10, function(g){
 record <- list(EK = 1)
 record$EK<-round(runif(1,0,1)*10)
 lobstr::obj_addr(record$EK)
})

Okay, so I can take that off my list to worry about. Thanks again for the fast reply.

Marco Smolla
  • 188
  • 5