2013-08-27 9 views
21

Ich war interessiert mit der Speicherauslastung der Matrix in R, wenn ich etwas seltsames beobachtet. In einer Schleife habe ich die Anzahl der Spalten einer Matrix erhöht und für jeden Schritt die Objektgröße berechnet.Gleiche Speicherauslastung der Matrix mit unterschiedlicher Größe

x=10 
size=matrix(1:x,x,2) 

for (i in c(1:x)){ 
    m = matrix(1,2, i) 
    size[i,2]=object.size(m) 
} 

Aufgetragen mein Ergebnis

plot(size[,1],size[,2]) 

enter image description here

Es scheint, dass eine Matrix mit 2 Reihen und 5,6,7 oder 8 Spalten den gleichen Speicher verwendet. Wie können wir das erklären?

+2

Th: alle Konstanten auf der y-Achse um 160, können Sie die Diskontinuität entspricht genau den Sprung vom kleinen Vektor Pool zum großen Vektor-Pool sehen is ist nicht verwandt mit Matrizen (die in R sowieso Vektoren sind). Sehen Sie sich 'sizes Roland

+1

Betrachten Sie das Zeichen 'm <- Matrix ('a', 2, i)' und die ganze Zahl 'm <- Matrix (1L, 2, i)' und 'm <- Matrix (TRUE, 2, i)' - oder interessanterweise was @Roland vorgeschlagen hat (für Zeichen, Integer und logische Vektoren). – mnel

+0

@ SimonO101 Hadley erklärte es im Chat. Ich hoffe, er findet die Zeit, eine richtige Antwort zu schreiben. ZB lesen Sie [diesen Abschnitt in R-Exts] (http://cran.r-project.org/doc/manuals/R-exts.html#Profiling-R-code-for-memory-use). – Roland

Antwort

35

Um zu verstehen, was hier los ist, müssen Sie ein wenig über den Speicher-Overhead mit Objekten in R. Jedes Objekt, auch ein Objekt ohne Daten zugeordnet wissen, hat 40 Byte Daten zugeordnet:

x0 <- numeric() 
object.size(x0) 
# 40 bytes 

Dieser Speicher, die Art des Objekts zu speichern, verwendet wird (wie durch typeof() zurückgegeben) und andere Metadaten für die Speicherverwaltung benötigt.

Nachdem Sie diesen Overhead ignoriert haben, können Sie erwarten, dass die Speichernutzung eines Vektors proportional zur Länge des Vektors ist. Lassen Sie uns prüfen, ob aus mit ein paar Stellplätze:

sizes <- sapply(0:50, function(n) object.size(seq_len(n))) 
plot(c(0, 50), c(0, max(sizes)), xlab = "Length", ylab = "Bytes", 
    type = "n") 
abline(h = 40, col = "grey80") 
abline(h = 40 + 128, col = "grey80") 
abline(a = 40, b = 4, col = "grey90", lwd = 4) 
lines(sizes, type = "s") 

Memory usage of vectors

Es ist wie die Speichernutzung aussieht, ist in etwa proportional zur Länge des Vektors, aber es ist eine große Diskontinuität bei 168 Bytes und kleine Diskontinuitäten jeder wenige Schritte. Die große Diskontinuität liegt darin, dass R zwei Speicherpools für Vektoren hat: kleine Vektoren, die von R verwaltet werden, und große Vektoren, die vom Betriebssystem verwaltet werden (Dies ist eine Leistungsoptimierung, da die Zuweisung großer Mengen an Speicher teuer ist). Kleine Vektoren nur 8 sein können, 16, 32, 48, 64 oder 128 Bytes lang ist, das, sobald wir das 40-Byte-Overhead entfernen, ist genau das, was wir sehen:

sizes - 40 
# [1] 0 8 8 16 16 32 32 32 32 48 48 48 48 64 64 64 64 128 128 128 128 
# [22] 128 128 128 128 128 128 128 128 128 128 128 128 136 136 144 144 152 152 160 160 168 
# [43] 168 176 176 184 184 192 192 200 200 

Der Schritt von 64 bis 128 bewirkt, dass die großen Schritt, dann, wenn wir in den großen Vektor-Pool gekreuzt haben, Vektoren wird in Blöcken von 8 Byte zugewiesen (Speicher kommt in Einheiten von einer bestimmten Größe, und R kann für eine halbe Einheit nicht fragen):

# diff(sizes) 
# [1] 8 0 8 0 16 0 0 0 16 0 0 0 16 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 
# [29] 0 0 0 0 8 0 8 0 8 0 8 0 8 0 8 0 8 0 8 0 8 0 

Also, wie verhält sich dieses Verhalten zu dem, was Sie mit Matrizen sehen? Nun, zuerst müssen wir den Overhead mit einer Matrix assoziiert suchen:

xv <- numeric() 
xm <- matrix(xv) 

object.size(xm) 
# 200 bytes 

object.size(xm) - object.size(xv) 
# 160 bytes 

So eine Matrix ein zusätzlichen 160 Byte Speicherplatz im Vergleich zu einem Vektor benötigt. Warum 160 Bytes?Es ist, weil eine Matrix, die ein dim Attribut mit zwei ganzen Zahlen hat und Attribute in einem pairlist (eine ältere Version von list()) gespeichert sind:

object.size(pairlist(dims = c(1L, 1L))) 
# 160 bytes 

Wenn wir die vorherige Handlung wieder ziehen Matrizen anstelle von Vektoren und Erhöhung

msizes <- sapply(0:50, function(n) object.size(as.matrix(seq_len(n)))) 
plot(c(0, 50), c(160, max(msizes)), xlab = "Length", ylab = "Bytes", 
    type = "n") 
abline(h = 40 + 160, col = "grey80") 
abline(h = 40 + 160 + 128, col = "grey80") 
abline(a = 40 + 160, b = 4, col = "grey90", lwd = 4) 
lines(msizes, type = "s") 

Memory usage of matrices

+5

+1 interessante und gute Antwort. Danke hadley –

+1

Danke hadley. Das ist eine perfekte Antwort. – Bangyou

7

Dies scheint nur für einen sehr spezifischen Bereich von Spalten am kleinen Ende passieren. Mit Blick auf Matrizen mit 1-100 Spalten sehe ich folgendes:

enter image description here

Ich sehe keine andere Hochebenen, auch wenn ich die Anzahl der Spalten erhöhen zu sagen, 10000:

enter image description here

Fasziniert, ich habe weiter an etwas sah, Ihren Code in einer Funktion setzen:

sizes <- function(nrow, ncol) { 
    size=matrix(1:ncol,ncol,2) 
    for (i in c(1:ncol)){ 
    m = matrix(1,nrow, i) 
    size[i,2]=object.size(m) 
    } 
    plot(size[,1], size[,2]) 
    size 
} 

Interessanterweise wir st ill dieses Plateau und gerade Linie in niedrigeren Zahlen sehen, wenn wir die Anzahl der Zeilen, mit dem Plateau Schrumpfung und rückwärts bewegt, erhöhen, bevor sie schließlich durch die Zeit zu einer geraden Linie passen wir nrow=8 getroffen:

Size of matrices with 3-8 rows with 10 columns:

Anzeige dass dies für einen sehr spezifischen Bereich für die Anzahl von Zellen in einer Matrix geschieht; 9-16.

Speicherzuordnung

Wie @Hadley in seinem Kommentar darauf hingewiesen, gibt es ein ähnliches thread on memory allocation of vectors. Das ergibt die Formel: 40 + 8 * floor(n/2) für numeric Vektoren der Größe n.

Für Matrizen ist der Overhead etwas anders, und die Stepping-Beziehung hält nicht (wie in meinen Plots zu sehen). Stattdessen habe ich die Formel 208 + 8 * n Bytes kommen, wo n die Anzahl der Zellen in der Matrix (nrow * ncol), sofern n zwischen 9 und 16:

Matrixgröße - 208 Bytes für "double" Matrices, 1 Zeile, 1 -20 Spalten:

> sapply(1:20, function(x) { object.size(matrix(1, 1, x)) })-208 
[1] 0 8 24 24 40 40 56 56 120 120 120 120 120 120 120 120 128 136 144 
[20] 152 

JEDOCH.Wenn wir die Art der Matrix zu Integer oder Logical verändern, haben wir das Verhalten schrittweise in der Speicherzuordnung in dem Thread oben beschrieben unter:

Matrixgröße - 208 Bytes für "integer" Matrices 1 Zeile 1-20 Spalten:

> sapply(1:20, function(x) { object.size(matrix(1L, 1, x)) })-208 
[1] 0 0 8 8 24 24 24 24 40 40 40 40 56 56 56 56 120 120 120 
[20] 120 

Ebenso für "logical" Matrizen:

> sapply(1:20, function(x) { object.size(matrix(1L, 1, x)) })-208 
[1] 0 0 8 8 24 24 24 24 40 40 40 40 56 56 56 56 120 120 120 
[20] 120 

es ist überraschend, dass wir das gleiche Verhalten mit einer Matrix von Typ nicht sehen double, da es mit nur "numeric" Vektor ein dim Attribut beigefügt (R lang specification).

Der große Schritt, den wir bei der Speicherzuweisung sehen, kommt von R mit zwei Speicherpools, einer für kleine Vektoren und einer für große Vektoren, und das ist der Ort, wo der Sprung gemacht wird. Hadley Wickham erklärt dies ausführlich in seiner Antwort.

5

Betrachten Sie den numerischen Vektor mit der Größe von 1 bis 20, ich habe diese Zahl.

x=20 
size=matrix(1:x,x,2) 
for (i in c(1:x)){ 
    m = rep(1, i) 
    size[i,2]=object.size(m) 
} 

plot(size[,1],size[,2]) 

enter image description here

+0

Hah, das ist eine viel klügere Art, hineinzugraben! –

Verwandte Themen