Skip to content

Instantly share code, notes, and snippets.

@jonocarroll
Last active November 6, 2023 12:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonocarroll/41f88c747f9b841c757a2909f9e92bcd to your computer and use it in GitHub Desktop.
Save jonocarroll/41f88c747f9b841c757a2909f9e92bcd to your computer and use it in GitHub Desktop.
Understanding R's copy-on-modify semantics wrt the symbol table
## Modify a vector in the workspace; x is a user-accessible symbol
x <- 42
.Internal(inspect(x))
# @5631a3a19e20 14 REALSXP g0c1 [REF(5)] (len=1, tl=0) 42
x[1] <- 43 # modification causes a copy (address changes)
.Internal(inspect(x))
# @5631a36c1cb8 14 REALSXP g0c1 [REF(4)] (len=1, tl=0) 43
## Modify a vector inside a function; user cannot access y
f <- function() {
y <- 42 # this value is not accessible outside of the function
.Internal(inspect(y))
y[1] <- 43 # modification causes a copy (address changes)
.Internal(inspect(y))
rm(y)
invisible()
}
f()
# @5631a192c090 14 REALSXP g0c1 [REF(2)] (len=1, tl=0) 42
# @5631a1863f10 14 REALSXP g0c1 [REF(1)] (len=1, tl=0) 43
## Compile? Now they're the same address
g <- compiler::cmpfun(f)
g()
# @56319daa6728 14 REALSXP g0c1 [REF(1)] (len=1, tl=0) 42
# @56319daa6728 14 REALSXP g0c1 [REF(1)] (len=1, tl=0) 43
## The bytecode appears to be exactly the same between f and g
# rbytecode::dis(f)
# rbytecode::dis(g)
# diffobj::diffObj(rbytecode::dis(f), rbytecode::dis(g))
identical(rbytecode::dis(f), rbytecode::dis(g))
# TRUE
@jonocarroll
Copy link
Author

> rbytecode::dis(f)
   depth pc opcode               op      args
1      0  1     16          LDCONST        42
2      0  3     22           SETVAR         y
3      0  5      4              POP      NULL
4      0  6    123        BASEGUARD   @label1
5      0  9     27   GETINTLBUILTIN   inspect
6      0 11     20           GETVAR         y
7      0 13     33          PUSHARG      NULL
8      0 14     39      CALLBUILTIN      NULL
9      0 NA     NA          @label1      NULL
10     0 16      4              POP      NULL
11     0 17     16          LDCONST        43
12     0 19     61      STARTASSIGN         y
13     0 21    105 STARTSUBASSIGN_N   @label2
14     0 24     16          LDCONST         1
15     0 26     86     VECSUBASSIGN      NULL
16     0 NA     NA          @label2      NULL
17     0 28     62        ENDASSIGN         y
18     0 30      4              POP      NULL
19     0 31    123        BASEGUARD   @label3
20     0 34     27   GETINTLBUILTIN   inspect
21     0 36     20           GETVAR         y
22     0 38     33          PUSHARG      NULL
23     0 39     39      CALLBUILTIN      NULL
24     0 NA     NA          @label3      NULL
25     0 41      4              POP      NULL
26     0 42     23           GETFUN        rm
27     0 44     29         MAKEPROM      NULL
28     1  1     20           GETVAR         y
29     1  3      1           RETURN      NULL
30     0 NA     NA      ENDMAKEPROM      NULL
31     0 46     38             CALL      NULL
32     0 48      4              POP      NULL
33     0 49    123        BASEGUARD   @label4
34     0 52     26       GETBUILTIN invisible
35     0 54     39      CALLBUILTIN      NULL
36     0 NA     NA          @label4      NULL
37     0 56      1           RETURN      NULL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment