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.