Gestión avanzada de memoria en R: tracemem (II)

He leído estos días el capítulo 14 de The Art of R Programming que trata problemas y trucos para mejorar el rendimiento de R en términos de velocidad y memoria. Menciona la función tracemem de la que nos ocupamos el otro día.

Menciona el capítulo cómo uno de los estranguladores del rendimiento de R es su política de copiar al cambiar (copy-on-change). Generalmente, cuando modificamos un objeto, R realiza una copia íntegra de él (¿y qué pasa si realizamos pequeñas modificaciones en un objeto muy grande?):

m <- 1:10
tracemem(m)
# [1] "<0x16952c0>"
m[1] <- 8
# tracemem[0x16952c0 -> 0x10cd228]:

Sin embargo el libro menciona cómo, a pesar de la política copiar al cambiar, hay casos en los que R es lo suficientemente inteligente como para modificar sólo la parte afectada por el cambio:

z <- runif(10)
tracemem(z)
# [1] "<0x1044ff0>"
z[1] <- 8
tracemem(z)
# [1] "<0x1044ff0>"

En este caso, no se copia el objeto: sólo se modifica una de las entradas del mismo.

Pero, ¿por qué en este segundo ejemplo no hay copia y el en primero sí? El motivo es el tipo de almacenamiento interno de R:

m <- 1:10
typeof(m)
# [1] "integer"
tracemem(m)
# [1] "<0x14e83f8>"
m[1] <- 8
# tracemem[0x14e83f8 -> 0x14e8450]:
# tracemem[0x14e8450 -> 0x1045140]:
typeof(m)
# [1] "double"

Efectivamente, hay una copia, pero precisamente porque la asignación implica un cambio (implícito) de manera de almacenar datos. Aunque se podría hacer también

m <- 1:10
tracemem(m)
# [1] "<0x14e8558>"
m[1] <- 8L

para evitar la copia.