Thủ thuật để quản lý bộ nhớ khả dụng trong phiên R


490

Mọi người sử dụng thủ thuật nào để quản lý bộ nhớ khả dụng của phiên R tương tác? Tôi sử dụng các chức năng dưới đây [dựa trên các bài đăng của Petr Pikal và David Hinds vào danh sách trợ giúp năm 2004] để liệt kê (và / hoặc sắp xếp) các đối tượng lớn nhất và thỉnh thoảngrm() một số trong số chúng. Nhưng cho đến nay, giải pháp hiệu quả nhất là ... chạy Linux 64 bit với bộ nhớ lớn.

Bất kỳ thủ thuật tốt đẹp nào folks muốn chia sẻ? Một bài viết, xin vui lòng.

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.dim)
    names(out) <- c("Type", "Size", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}
# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

Lưu ý, tôi KHÔNG nghi ngờ điều đó, nhưng việc sử dụng đó là gì? Tôi khá mới đối với các vấn đề về bộ nhớ trong R, nhưng tôi đang gặp một số vấn đề gần đây (đó là lý do tại sao tôi tìm kiếm bài đăng này :) - vì vậy tôi mới bắt đầu với tất cả điều này. Làm thế nào điều này giúp công việc hàng ngày của tôi?
Matt Bannert

4
nếu bạn muốn xem các đối tượng trong một hàm, bạn phải sử dụng: lsos (pos = môi trường ()), nếu không nó sẽ chỉ hiển thị các biến toàn cục. Để ghi vào lỗi tiêu chuẩn: write.table (lsos (pos = môi trường ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn

Tại sao linux 64 bit mà không phải Windows 64 bit? Sự lựa chọn của HĐH có tạo ra sự khác biệt không hề nhỏ khi tôi có 32GB ram để sử dụng không?
Jase

3
@pepsimax: Cái này đã được đóng gói trong multilevelPSAgói . Gói được thiết kế cho một cái gì đó khác, nhưng bạn có thể sử dụng chức năng từ đó mà không cần tải gói bằng cách nói requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Hoặc trong Dmiscgói (không phải trên CRAN).
krlmlr

1
Nếu tập dữ liệu có kích thước có thể quản lý, tôi thường đến R studio> Môi trường> Chế độ xem lưới. Tại đây bạn có thể xem và sắp xếp tất cả các mục trong môi trường hiện tại của bạn dựa trên kích thước.
kRazzy R

Câu trả lời:


197

Đảm bảo bạn ghi lại công việc của mình trong một kịch bản có thể tái tạo. Thỉnh thoảng, mở lại R, rồi đến source()kịch bản của bạn. Bạn sẽ dọn sạch mọi thứ bạn không còn sử dụng nữa và vì lợi ích bổ sung sẽ kiểm tra mã của bạn.


58
Chiến lược của tôi là phá vỡ các tập lệnh của tôi dọc theo các dòng tải.R và do.R, trong đó load.R có thể mất khá nhiều thời gian để tải dữ liệu từ các tệp hoặc cơ sở dữ liệu và thực hiện bất kỳ quá trình tiền xử lý / hợp nhất tối thiểu nào dữ liệu đó. Dòng cuối cùng của tải.R là một cái gì đó để lưu trạng thái không gian làm việc. Sau đó, do.R là Scratchpad của tôi, theo đó tôi xây dựng các chức năng phân tích của mình. Tôi thường xuyên tải lại do.R (có hoặc không tải lại trạng thái không gian làm việc từ load.R khi cần).
Josh Reich

32
Đó là một kỹ thuật tốt. Khi tập tin được chạy theo một thứ tự nhất định như vậy, tôi thường tiền tố họ với một số: 1-load.r, 2-explore.r, 3-model.r- cách mà nó là hiển nhiên đối với những người khác rằng có một số mặt hàng.
hadley

4
Tôi không thể trở lại ý tưởng này đủ. Tôi đã dạy R cho một vài người và đây là một trong những điều đầu tiên tôi nói. Điều này cũng áp dụng cho bất kỳ ngôn ngữ nào khi phát triển kết hợp REPL và tệp đang được chỉnh sửa (ví dụ Python). rm (ls = list ()) và source () cũng hoạt động, nhưng mở lại thì tốt hơn (các gói cũng bị xóa).
Vince

53
Việc câu trả lời được bình chọn hàng đầu liên quan đến việc khởi động lại R là sự chỉ trích tồi tệ nhất về R có thể.
sds

7
@ MartínBel chỉ loại bỏ các đối tượng được tạo trong môi trường toàn cầu. Nó không dỡ gói hoặc đối tượng S4 hoặc nhiều thứ khác.
hadley

160

Tôi sử dụng gói data.table . Với :=toán tử của nó, bạn có thể:

  • Thêm cột theo tham chiếu
  • Sửa đổi tập hợp con của các cột hiện có theo tham chiếu và theo nhóm theo tham chiếu
  • Xóa cột bằng tham chiếu

Không có thao tác nào trong số này sao chép (có khả năng lớn) data.tablecả, thậm chí không chỉ một lần.

  • Tập hợp cũng đặc biệt nhanh vì data.tablesử dụng bộ nhớ làm việc ít hơn nhiều.

Liên kết liên quan :


109

Xem cái này trên một bài đăng trên twitter và nghĩ rằng đó là một chức năng tuyệt vời của Dirk! Theo dõi câu trả lời của JD Long, tôi sẽ làm điều này để đọc thân thiện với người dùng:

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.prettysize <- napply(names, function(x) {
                           format(utils::object.size(x), units = "auto") })
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Length/Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()

Điều này dẫn đến kết quả như sau:

                      Type   Size PrettySize Length/Rows Columns
pca.res                 PCA 790128   771.6 Kb          7      NA
DF               data.frame 271040   264.7 Kb        669      50
factor.AgeGender   factanal  12888    12.6 Kb         12      NA
dates            data.frame   9016     8.8 Kb        669       2
sd.                 numeric   3808     3.7 Kb         51      NA
napply             function   2256     2.2 Kb         NA      NA
lsos               function   1944     1.9 Kb         NA      NA
load               loadings   1768     1.7 Kb         12       2
ind.sup             integer    448  448 bytes        102      NA
x                 character     96   96 bytes          1      NA

LƯU Ý: Phần chính tôi đã thêm là (một lần nữa, được điều chỉnh từ câu trả lời của JD):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })

chức năng này có thể được thêm vào dplyr hoặc một số gói khóa khác.
userJT

1
Đáng lưu ý rằng (ít nhất là với cơ sở-3.3.2) capture.outputkhông còn cần thiết nữa và obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })tạo ra đầu ra sạch. Trong thực tế, không loại bỏ nó tạo ra các trích dẫn không mong muốn trong đầu ra, tức là [1] "792.5 Mb"thay vì 792.5 Mb.
Nutle

@Nutle Tuyệt vời, tôi đã cập nhật mã phù hợp :)
Tony Breyal

Tôi cũng muốn thay đổi obj.class <- napply(names, function(x) as.character(class(x))[1])để obj.class <- napply(names, function(x) class(x)[1]) từ classluôn luôn trả về một vector của các nhân vật bây giờ (base-3.5.0).
DeltaIV

49

Tôi sử dụng tích cực subsettham số với chỉ chọn các biến cần thiết khi truyền các tệp dữ liệu cho data=đối số của hàm hồi quy. Nó sẽ dẫn đến một số lỗi nếu tôi quên thêm các biến vào cả công thức và select=vectơ, nhưng nó vẫn tiết kiệm rất nhiều thời gian do giảm sao chép đối tượng và giảm đáng kể dấu chân bộ nhớ. Giả sử tôi có 4 triệu bản ghi với 110 biến (và tôi cũng vậy.) Ví dụ:

# library(rms); library(Hmisc) for the cph,and rcs functions
Mayo.PrCr.rbc.mdl <- 
cph(formula = Surv(surv.yr, death) ~ age + Sex + nsmkr + rcs(Mayo, 4) + 
                                     rcs(PrCr.rat, 3) +  rbc.cat * Sex, 
     data = subset(set1HLI,  gdlab2 & HIVfinal == "Negative", 
                           select = c("surv.yr", "death", "PrCr.rat", "Mayo", 
                                      "age", "Sex", "nsmkr", "rbc.cat")
   )            )

Bằng cách thiết lập bối cảnh và chiến lược: gdlab2biến là một vectơ logic được xây dựng cho các đối tượng trong bộ dữ liệu có tất cả các giá trị bình thường hoặc gần như bình thường cho một loạt các xét nghiệm trong phòng thí nghiệm và HIVfinallà một vectơ ký tự tóm tắt xét nghiệm sơ bộ và xác nhận HIV .


48

Tôi yêu tập lệnh .ls.objects () của Dirk nhưng tôi vẫn nheo mắt để đếm các ký tự trong cột kích thước. Vì vậy, tôi đã thực hiện một số hack xấu xí để làm cho nó hiện diện với định dạng đẹp cho kích thước:

.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.prettysize <- sapply(obj.size, function(r) prettyNum(r, big.mark = ",") )
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size,obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
        out <- out[c("Type", "PrettySize", "Rows", "Columns")]
        names(out) <- c("Type", "Size", "Rows", "Columns")
    if (head)
        out <- head(out, n)
    out
}

34

Đó là một mẹo hay.

Một đề xuất khác là sử dụng các đối tượng hiệu quả bộ nhớ bất cứ khi nào có thể: ví dụ: sử dụng ma trận thay vì data.frame.

Điều này không thực sự giải quyết việc quản lý bộ nhớ, nhưng một chức năng quan trọng không được biết đến rộng rãi là memory.limit (). Bạn có thể tăng mặc định bằng lệnh này, memory.limit (size = 2500), trong đó kích thước tính bằng MB. Như Dirk đã đề cập, bạn cần sử dụng 64-bit để tận dụng lợi thế này.


25
Điều này không chỉ áp dụng cho Windows sao?
Christopher DuBois

4
> memory.limit () [1] Thông báo cảnh báo Inf: 'memory.limit ()' dành riêng cho Windows
LJT

Việc sử dụng tibble thay vì data.frame có giúp chúng tôi tiết kiệm bộ nhớ tốt hơn không?

32

Tôi khá thích chức năng đối tượng được cải thiện được phát triển bởi Dirk. Phần lớn thời gian, một đầu ra cơ bản hơn với tên và kích thước đối tượng là đủ cho tôi. Đây là một chức năng đơn giản hơn với một mục tiêu tương tự. Việc sử dụng bộ nhớ có thể được sắp xếp theo thứ tự abc hoặc theo kích thước, có thể giới hạn ở một số đối tượng nhất định và có thể được sắp xếp tăng dần hoặc giảm dần. Ngoài ra, tôi thường làm việc với dữ liệu là 1GB +, vì vậy chức năng thay đổi đơn vị tương ứng.

showMemoryUse <- function(sort="size", decreasing=FALSE, limit) {

  objectList <- ls(parent.frame())

  oneKB <- 1024
  oneMB <- 1048576
  oneGB <- 1073741824

  memoryUse <- sapply(objectList, function(x) as.numeric(object.size(eval(parse(text=x)))))

  memListing <- sapply(memoryUse, function(size) {
        if (size >= oneGB) return(paste(round(size/oneGB,2), "GB"))
        else if (size >= oneMB) return(paste(round(size/oneMB,2), "MB"))
        else if (size >= oneKB) return(paste(round(size/oneKB,2), "kB"))
        else return(paste(size, "bytes"))
      })

  memListing <- data.frame(objectName=names(memListing),memorySize=memListing,row.names=NULL)

  if (sort=="alphabetical") memListing <- memListing[order(memListing$objectName,decreasing=decreasing),] 
  else memListing <- memListing[order(memoryUse,decreasing=decreasing),] #will run if sort not specified or "size"

  if(!missing(limit)) memListing <- memListing[1:limit,]

  print(memListing, row.names=FALSE)
  return(invisible(memListing))
}

Và đây là một số ví dụ đầu ra:

> showMemoryUse(decreasing=TRUE, limit=5)
      objectName memorySize
       coherData  713.75 MB
 spec.pgram_mine  149.63 kB
       stoch.reg  145.88 kB
      describeBy    82.5 kB
      lmBandpass   68.41 kB

30

Tôi không bao giờ lưu một không gian làm việc R. Tôi sử dụng tập lệnh nhập và tập lệnh dữ liệu và xuất bất kỳ đối tượng dữ liệu đặc biệt lớn nào mà tôi không muốn tạo lại thường xuyên thành tệp. Bằng cách này, tôi luôn bắt đầu với một không gian làm việc mới và không cần dọn dẹp các vật thể lớn. Đó là một chức năng rất tốt mặc dù.


30

Thật không may, tôi không có thời gian để kiểm tra nó một cách rộng rãi nhưng đây là một mẹo về trí nhớ mà tôi chưa từng thấy trước đây. Đối với tôi, bộ nhớ cần thiết đã giảm hơn 50%. Khi bạn đọc nội dung vào R với ví dụ read.csv, chúng yêu cầu một lượng bộ nhớ nhất định. Sau này, bạn có thể lưu chúng với save("Destinationfile",list=ls()) Lần tiếp theo bạn mở R, bạn có thể sử dụng load("Destinationfile") Bây giờ việc sử dụng bộ nhớ có thể đã giảm. Sẽ thật tuyệt nếu ai đó có thể xác nhận liệu điều này có tạo ra kết quả tương tự với một tập dữ liệu khác không.


4
vâng, tôi đã trải nghiệm tương tự. Việc sử dụng bộ nhớ giảm thậm chí đến 30% trong trường hợp của tôi. Bộ nhớ 1,5 GB đã sử dụng, được lưu vào .RData (~ 30MB). Phiên mới sau khi tải .RData sử dụng ít hơn 500 MB bộ nhớ.
f3lix

Tôi đã thử với 2 bộ dữ liệu (100MB và 2.7GB) được tải vào data.table bằng cách sử dụng fread, sau đó được lưu vào .RData. Các tệp RData thực sự nhỏ hơn khoảng 70% nhưng sau khi tải lại, bộ nhớ được sử dụng hoàn toàn giống nhau. Hy vọng thủ thuật này sẽ làm giảm dấu chân bộ nhớ ... tôi có thiếu thứ gì không?
NoviceProg 18/03/2015

@NoviceProg Tôi không nghĩ rằng bạn đang thiếu thứ gì đó, nhưng đó là một mẹo, tôi đoán nó sẽ không hoạt động cho tất cả các tình huống. Trong trường hợp của tôi, bộ nhớ sau khi tải lại thực sự bị giảm như mô tả.
Dennis Jaheruddin

6
@NoviceProg Một vài điều. Đầu tiên, fread, theo sau uy tín của data.table có thể là bộ nhớ hiệu quả hơn trong việc tải các tệp so với read.csv. Thứ hai, những người tiết kiệm bộ nhớ mà mọi người lưu ý ở đây chủ yếu phải làm với kích thước bộ nhớ của quy trình R (mở rộng để giữ các đối tượng và rút lại khi quá trình thu gom rác diễn ra). Tuy nhiên, bộ sưu tập rác không phải lúc nào cũng giải phóng tất cả RAM trở lại HĐH. Dừng phiên R và tải vật phẩm từ nơi nó được lưu trữ sẽ giải phóng càng nhiều RAM càng tốt ... nhưng nếu chi phí quá nhỏ bắt đầu bằng ... không tăng.
russellpierce

27

Để minh họa thêm cho chiến lược chung là khởi động lại thường xuyên, chúng ta có thể sử dụng littler cho phép chúng ta chạy các biểu thức đơn giản trực tiếp từ dòng lệnh. Dưới đây là một ví dụ đôi khi tôi sử dụng thời gian BLAS khác nhau cho một crossprod đơn giản.

 r -e'N<-3*10^3; M<-matrix(rnorm(N*N),ncol=N); print(system.time(crossprod(M)))'

Tương tự như vậy,

 r -lMatrix -e'example(spMatrix)'

tải gói Matrix (thông qua công tắc --packages | -l) và chạy các ví dụ về chức năng spMatrix. Vì r luôn bắt đầu 'mới', phương pháp này cũng là một thử nghiệm tốt trong quá trình phát triển gói.

Cuối cùng nhưng không kém phần quan trọng cũng hoạt động tuyệt vời cho chế độ hàng loạt tự động trong các tập lệnh bằng cách sử dụng tiêu đề shebang '#! / Usr / bin / r'. Rupcript là một thay thế trong đó littler không có sẵn (ví dụ trên Windows).


23

Đối với cả mục đích tốc độ và bộ nhớ, khi xây dựng khung dữ liệu lớn thông qua một số bước phức tạp, tôi sẽ định kỳ chuyển nó (bộ dữ liệu đang thực hiện) sang đĩa, nối thêm bất cứ thứ gì có trước, sau đó khởi động lại nó . Bằng cách này, các bước trung gian chỉ hoạt động trên các khung dữ liệu nhỏ (điều này tốt, ví dụ, rbind làm chậm đáng kể với các đối tượng lớn hơn). Toàn bộ tập dữ liệu có thể được đọc lại vào cuối quá trình, khi tất cả các đối tượng trung gian đã bị xóa.

dfinal <- NULL
first <- TRUE
tempfile <- "dfinal_temp.csv"
for( i in bigloop ) {
    if( !i %% 10000 ) { 
        print( i, "; flushing to disk..." )
        write.table( dfinal, file=tempfile, append=!first, col.names=first )
        first <- FALSE
        dfinal <- NULL   # nuke it
    }

    # ... complex operations here that add data to 'dfinal' data frame  
}
print( "Loop done; flushing to disk and re-reading entire data set..." )
write.table( dfinal, file=tempfile, append=TRUE, col.names=FALSE )
dfinal <- read.table( tempfile )

17

Chỉ cần lưu ý rằng data.tablegói tables()dường như là một sự thay thế khá tốt cho .ls.objects()chức năng tùy chỉnh của Dirk (chi tiết trong các câu trả lời trước đó), mặc dù chỉ dành cho data.frames / bảng và không ví dụ như ma trận, mảng, danh sách.


điều này không liệt kê bất kỳ data.frames nào vì vậy nó không tuyệt vời
userJT

16
  1. Tôi may mắn và bộ dữ liệu lớn của tôi được lưu bằng công cụ trong "khối" (tập hợp con) khoảng 100 MB (nhị phân 32 bit). Do đó, tôi có thể thực hiện các bước xử lý trước (xóa các phần không chính xác, lấy mẫu xuống) theo tuần tự trước khi hợp nhất tập dữ liệu.

  2. Gọi gc ()"bằng tay" có thể giúp nếu kích thước của dữ liệu gần với bộ nhớ khả dụng.

  3. Đôi khi một thuật toán khác cần ít bộ nhớ hơn.
    Đôi khi có một sự đánh đổi giữa việc vector hóa và sử dụng bộ nhớ.
    so sánh: split& lapplyso với một forvòng lặp.

  4. Để phân tích dữ liệu nhanh chóng và dễ dàng, tôi thường làm việc trước tiên với một tập hợp con ngẫu nhiên nhỏ ( sample ()) của dữ liệu. Khi tập lệnh phân tích dữ liệu / .Rnw hoàn thành mã phân tích dữ liệu và dữ liệu hoàn chỉnh sẽ đến máy chủ tính toán để qua đêm / cuối tuần / ... tính toán.


11

Việc sử dụng các môi trường thay vì danh sách để xử lý các bộ sưu tập các đối tượng chiếm một lượng đáng kể bộ nhớ làm việc.

Lý do: mỗi lần một phần tử của listcấu trúc được sửa đổi, toàn bộ danh sách sẽ được sao chép tạm thời. Điều này trở thành một vấn đề nếu yêu cầu lưu trữ của danh sách là khoảng một nửa bộ nhớ làm việc khả dụng, vì sau đó dữ liệu phải được hoán đổi sang đĩa cứng chậm. Mặt khác, môi trường không phải chịu hành vi này và chúng có thể được đối xử tương tự như danh sách.

Đây là một ví dụ:

get.data <- function(x)
{
  # get some data based on x
  return(paste("data from",x))
}

collect.data <- function(i,x,env)
{
  # get some data
  data <- get.data(x[[i]])
  # store data into environment
  element.name <- paste("V",i,sep="")
  env[[element.name]] <- data
  return(NULL)  
}

better.list <- new.env()
filenames <- c("file1","file2","file3")
lapply(seq_along(filenames),collect.data,x=filenames,env=better.list)

# read/write access
print(better.list[["V1"]])
better.list[["V2"]] <- "testdata"
# number of list elements
length(ls(better.list))

Cùng với các cấu trúc như big.matrixhoặc data.tablecho phép thay đổi nội dung của chúng tại chỗ, có thể đạt được mức sử dụng bộ nhớ rất hiệu quả.


6
Điều này không còn đúng nữa: từ R nâng cao của Hadley , "Thay đổi thành R 3.1.0 đã khiến việc sử dụng [môi trường] này thực sự ít quan trọng hơn vì sửa đổi danh sách không còn tạo ra một bản sao sâu."
petrelharp

8

Các llchức năng trong gDatagói có thể hiển thị việc sử dụng bộ nhớ của từng đối tượng là tốt.

gdata::ll(unit='MB')

Không có trên hệ thống của tôi: Phiên bản R 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-bit), gdata_2.13.3, gtools_3.4.1.
krlmlr

Bạn đúng tôi kiểm tra nó một khi nó được đặt hàng một cách tình cờ!
dùng1436187

1
vui lòng sửa đổi chức năng để sử dụng Gb, Mb
userJT

7

Nếu bạn thực sự muốn tránh rò rỉ, bạn nên tránh tạo ra bất kỳ vật thể lớn nào trong môi trường toàn cầu.

Những gì tôi thường làm là có một chức năng thực hiện công việc và trả về NULL- tất cả dữ liệu được đọc và thao tác trong chức năng này hoặc các chức năng khác mà nó gọi.


7

Chỉ với 4GB RAM (chạy Windows 10, do đó, hãy tạo khoảng 2 GB trở lên thực tế 1GB) tôi đã phải thực sự cẩn thận với việc phân bổ.

Tôi sử dụng data.table gần như độc quyền.

Hàm 'fread' cho phép bạn đặt thông tin theo tên trường khi nhập; chỉ nhập các trường thực sự cần thiết để bắt đầu. Nếu bạn đang sử dụng cơ sở R đọc, hãy bỏ qua các cột giả ngay sau khi nhập.

Như 42- gợi ý, khi nào có thể tôi sẽ tập hợp con trong các cột ngay sau khi nhập thông tin.

Tôi thường xuyên rm () các đối tượng từ môi trường ngay khi chúng không còn cần thiết, ví dụ như trên dòng tiếp theo sau khi sử dụng chúng để tập hợp một thứ khác và gọi gc ().

'fread' và 'fwrite' từ data.table có thể rất nhanh bằng cách so sánh với cơ sở R đọc và ghi.

Như kpierce8 gợi ý, tôi hầu như luôn luôn viết mọi thứ ra khỏi môi trường và đưa nó trở lại, ngay cả với hàng ngàn / hàng trăm ngàn tệp nhỏ để vượt qua. Điều này không chỉ giữ cho môi trường 'sạch' và giữ cho việc cấp phát bộ nhớ ở mức thấp mà có thể do thiếu RAM trầm trọng, R có xu hướng thường xuyên gặp sự cố trên máy tính của tôi; thực sự thường xuyên Có thông tin được sao lưu trên chính ổ đĩa khi mã tiến triển qua các giai đoạn khác nhau có nghĩa là tôi không phải bắt đầu ngay từ đầu nếu nó gặp sự cố.

Kể từ năm 2017, tôi nghĩ rằng các ổ SSD nhanh nhất đang chạy khoảng vài GB mỗi giây thông qua cổng M2. Tôi có ổ SSD 50 GB Kingston V300 (550MB / s) thực sự cơ bản mà tôi sử dụng làm ổ đĩa chính (có Windows và R trên đó). Tôi giữ tất cả các thông tin số lượng lớn trên một đĩa WD 500GB giá rẻ. Tôi di chuyển các tập dữ liệu sang SSD khi tôi bắt đầu làm việc với chúng. Điều này, kết hợp với 'fread'ing và' fwrite'ing mọi thứ đã hoạt động rất tốt. Tôi đã thử sử dụng 'ff' nhưng thích cái trước hơn. Tốc độ đọc / ghi 4K có thể tạo ra vấn đề với điều này; Sao lưu một phần tư triệu tệp 1k (trị giá 250 MB) từ SSD sang đĩa có thể mất nhiều giờ. Theo như tôi biết, vẫn chưa có gói R nào có thể tự động tối ưu hóa quy trình 'chunkization'; ví dụ: xem người dùng có bao nhiêu RAM kiểm tra tốc độ đọc / ghi của RAM / tất cả các ổ đĩa được kết nối và sau đó đề xuất giao thức 'chunkization' tối ưu. Điều này có thể tạo ra một số cải tiến quy trình công việc / tối ưu hóa tài nguyên quan trọng; ví dụ: chia nó thành ... MB cho ram -> chia nó thành ... MB cho SSD -> chia nó thành ... MB trên đĩa -> chia nó thành ... MB trên băng. Nó có thể lấy mẫu các tập dữ liệu trước để cung cấp cho nó một thanh đo thực tế hơn để làm việc.

Rất nhiều trong những vấn đề tôi đã làm việc trên R liên quan đến hình thành sự kết hợp và hoán vị cặp, gấp ba vv, mà chỉ làm cho có RAM hạn chế hơn một giới hạn như họ sẽ thường ít nhất là theo cấp số nhân mở rộng tại một số điểm. Điều này đã khiến tôi tập trung rất nhiều vào chất lượng trái ngược với số lượng thông tin bắt đầu với chúng, thay vì cố gắng làm sạch nó sau đó, và vào chuỗi hoạt động chuẩn bị thông tin bắt đầu (bắt đầu bằng hoạt động đơn giản nhất và tăng độ phức tạp); ví dụ: tập hợp con, sau đó hợp nhất / nối, sau đó hình thành tổ hợp / hoán vị, v.v.

Dường như có một số lợi ích khi sử dụng cơ sở R đọc và viết trong một số trường hợp. Chẳng hạn, việc phát hiện lỗi trong 'fread' rất tốt nên khó có thể cố gắng đưa thông tin thực sự lộn xộn vào R để bắt đầu dọn dẹp. Base R dường như cũng dễ dàng hơn rất nhiều nếu bạn đang sử dụng Linux. Base R dường như hoạt động tốt trong Linux, Windows 10 sử dụng ~ 20GB dung lượng đĩa trong khi Ubuntu chỉ cần vài GB, RAM cần thiết với Ubuntu thấp hơn một chút. Nhưng tôi đã nhận thấy một số lượng lớn các cảnh báo và lỗi khi cài đặt các gói của bên thứ ba trong (L) Ubuntu. Tôi không khuyên bạn nên đi quá xa khỏi (L) Ubuntu hoặc các bản phân phối cổ phiếu khác với Linux vì bạn có thể mất rất nhiều khả năng tương thích tổng thể, nó làm cho quá trình gần như vô nghĩa (tôi nghĩ rằng "sự thống nhất" sẽ bị hủy trong Ubuntu vào năm 2017 ).

Hy vọng rằng một số trong đó có thể giúp đỡ những người khác.


5

Điều này không thêm gì vào những điều trên, nhưng được viết theo phong cách đơn giản và được bình luận nặng nề mà tôi thích. Nó mang lại một bảng với các đối tượng được sắp xếp theo kích thước, nhưng không có một số chi tiết được đưa ra trong các ví dụ trên:

#Find the objects       
MemoryObjects = ls()    
#Create an array
MemoryAssessmentTable=array(NA,dim=c(length(MemoryObjects),2))
#Name the columns
colnames(MemoryAssessmentTable)=c("object","bytes")
#Define the first column as the objects
MemoryAssessmentTable[,1]=MemoryObjects
#Define a function to determine size        
MemoryAssessmentFunction=function(x){object.size(get(x))}
#Apply the function to the objects
MemoryAssessmentTable[,2]=t(t(sapply(MemoryAssessmentTable[,1],MemoryAssessmentFunction)))
#Produce a table with the largest objects first
noquote(MemoryAssessmentTable[rev(order(as.numeric(MemoryAssessmentTable[,2]))),])

5

Đây là một câu trả lời mới hơn cho câu hỏi cũ tuyệt vời này. Từ Advanced R của Hadley:

install.packages("pryr")

library(pryr)

object_size(1:10)
## 88 B

object_size(mean)
## 832 B

object_size(mtcars)
## 6.74 kB

( http://adv-r.had.co.nz/memory.html )


3

Nếu bạn đang làm việc trên Linux và muốn sử dụng một số quy trình và chỉ phải thực hiện các thao tác đọc trên một hoặc nhiều đối tượng lớn sử dụng makeForkClusterthay vì a makePSOCKcluster. Điều này cũng giúp bạn tiết kiệm thời gian gửi đối tượng lớn đến các quy trình khác.


2

Tôi thực sự đánh giá cao một số câu trả lời ở trên, sau @hadley và @Dirk đề nghị đóng R và ban hành sourcevà sử dụng dòng lệnh Tôi đưa ra một giải pháp hiệu quả cho tôi. Tôi đã phải đối phó với hàng trăm phổ khối, mỗi bộ chiếm khoảng 20 Mb bộ nhớ nên tôi đã sử dụng hai tập lệnh R, như sau:

Đầu tiên một trình bao bọc:

#!/usr/bin/Rscript --vanilla --default-packages=utils

for(l in 1:length(fdir)) {

   for(k in 1:length(fds)) {
     system(paste("Rscript runConsensus.r", l, k))
   }
}

với kịch bản này, về cơ bản tôi kiểm soát kịch bản chính của mình làm gì runConsensus.rvà tôi viết câu trả lời dữ liệu cho đầu ra. Với điều này, mỗi lần trình bao bọc gọi tập lệnh, có vẻ như R được mở lại và bộ nhớ được giải phóng.

Hy vọng nó giúp.


2

Cũng như các kỹ thuật quản lý bộ nhớ chung hơn được đưa ra trong các câu trả lời ở trên, tôi luôn cố gắng giảm kích thước của các đối tượng của mình càng xa càng tốt. Ví dụ, tôi làm việc với các ma trận rất lớn nhưng rất thưa thớt, nói cách khác là ma trận trong đó hầu hết các giá trị đều bằng không. Sử dụng gói 'Ma trận' (viết hoa quan trọng) tôi có thể giảm kích thước đối tượng trung bình của mình từ ~ 2GB xuống ~ 200MB chỉ đơn giản là:

my.matrix <- Matrix(my.matrix)

Gói Matrix bao gồm các định dạng dữ liệu có thể được sử dụng chính xác như ma trận thông thường (không cần thay đổi mã khác của bạn) nhưng có thể lưu trữ dữ liệu thưa thớt hiệu quả hơn nhiều, cho dù được tải vào bộ nhớ hoặc lưu vào đĩa.

Ngoài ra, các tệp thô tôi nhận được ở định dạng 'dài' trong đó mỗi điểm dữ liệu có các biến x, y, z, i. Hiệu quả hơn nhiều để chuyển đổi dữ liệu thành một x * y * zmảng kích thước chỉ với biến i.

Biết dữ liệu của bạn và sử dụng một chút ý nghĩa thông thường.


2

Mẹo để xử lý các đối tượng yêu cầu tính toán trung gian nặng: Khi sử dụng các đối tượng cần nhiều tính toán nặng và các bước trung gian để tạo, tôi thường thấy hữu ích khi viết một đoạn mã với hàm để tạo đối tượng và sau đó là một đoạn riêng biệt mã cung cấp cho tôi tùy chọn để tạo và lưu đối tượng dưới dạng rmdtệp hoặc tải bên ngoài từ rmdtệp mà tôi đã lưu trước đó. Điều này đặc biệt dễ thực hiện khi R Markdownsử dụng cấu trúc đoạn mã sau.

```{r Create OBJECT}

COMPLICATED.FUNCTION <- function(...) { Do heavy calculations needing lots of memory;
                                        Output OBJECT; }

```
```{r Generate or load OBJECT}

LOAD <- TRUE;
#NOTE: Set LOAD to TRUE if you want to load saved file
#NOTE: Set LOAD to FALSE if you want to generate and save

if(LOAD == TRUE) { OBJECT <- readRDS(file = 'MySavedObject.rds'); } else
                 { OBJECT <- COMPLICATED.FUNCTION(x, y, z);
                             saveRDS(file = 'MySavedObject.rds', object = OBJECT); }

```

Với cấu trúc mã này, tất cả những gì tôi cần làm là thay đổi LOADtùy thuộc vào việc tôi muốn tạo và lưu đối tượng hay tải trực tiếp từ tệp đã lưu. (Tất nhiên, tôi phải tạo nó và lưu nó lần đầu tiên, nhưng sau này tôi có tùy chọn tải nó.) Thiết lập LOAD = TRUEbỏ qua việc sử dụng chức năng phức tạp của tôi và tránh tất cả các tính toán nặng nề trong đó. Phương pháp này vẫn cần đủ bộ nhớ để lưu trữ đối tượng quan tâm, nhưng nó giúp bạn không phải tính toán mỗi khi bạn chạy mã của mình. Đối với các đối tượng đòi hỏi nhiều tính toán nặng của các bước trung gian (ví dụ: đối với các phép tính liên quan đến các vòng trên các mảng lớn), điều này có thể tiết kiệm một lượng đáng kể thời gian và tính toán.


1

Đang chạy

for (i in 1:10) 
    gc(reset = T)

theo thời gian cũng giúp R ​​giải phóng bộ nhớ không sử dụng nhưng vẫn không giải phóng bộ nhớ.


Không những gì forloop làm ở đây? Không có itrong gccuộc gọi.
Umaomamaomao

@qqq nó ở đó chỉ để tránh sao chép-dán gc(reset = T)chín lần
Marcelo Ventura

14
Nhưng tại sao bạn lại chạy nó 9 lần? (tò mò, không quan trọng)
Umaomamaomao

1

Bạn cũng có thể nhận được một số lợi ích bằng cách sử dụng đan và đưa tập lệnh của bạn vào các đoạn mã Rmd.

Tôi thường chia mã theo các phần khác nhau và chọn cái nào sẽ lưu điểm kiểm tra vào bộ đệm hoặc vào tệp RDS và

Ở đó bạn có thể đặt một đoạn được lưu thành "bộ đệm" hoặc bạn có thể quyết định chạy hoặc không một đoạn cụ thể. Theo cách này, trong lần chạy đầu tiên, bạn chỉ có thể xử lý "phần 1", một lần thực hiện khác bạn chỉ có thể chọn "phần 2", v.v.

Thí dụ:

part1
```{r corpus, warning=FALSE, cache=TRUE, message=FALSE, eval=TRUE}
corpusTw <- corpus(twitter)  # build the corpus
```
part2
```{r trigrams, warning=FALSE, cache=TRUE, message=FALSE, eval=FALSE}
dfmTw <- dfm(corpusTw, verbose=TRUE, removeTwitter=TRUE, ngrams=3)
```

Là một tác dụng phụ, điều này cũng có thể giúp bạn tiết kiệm một số vấn đề đau đầu về khả năng tái tạo :)


1

Dựa trên câu trả lời của @ Dirk và @ Tony, tôi đã cập nhật một chút. Kết quả đã được đưa ra [1]trước các giá trị kích thước đẹp, vì vậy tôi đã đưa ra capture.outputgiải pháp giải quyết vấn đề:

.ls.objects <- function (pos = 1, pattern, order.by,
                     decreasing=FALSE, head=FALSE, n=5) {
napply <- function(names, fn) sapply(names, function(x)
    fn(get(x, pos = pos)))
names <- ls(pos = pos, pattern = pattern)
obj.class <- napply(names, function(x) as.character(class(x))[1])
obj.mode <- napply(names, mode)
obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
obj.prettysize <- napply(names, function(x) {
    format(utils::object.size(x),  units = "auto") })
obj.size <- napply(names, utils::object.size)

obj.dim <- t(napply(names, function(x)
    as.numeric(dim(x))[1:2]))
vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
obj.dim[vec, 1] <- napply(names, length)[vec]
out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
if (!missing(order.by))
    out <- out[order(out[[order.by]], decreasing=decreasing), ]
if (head)
    out <- head(out, n)

return(out)
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()

-1

Tôi cố gắng giữ số lượng đối tượng nhỏ khi làm việc trong một dự án lớn hơn với nhiều bước trung gian. Vì vậy, thay vì tạo ra nhiều đối tượng duy nhất được gọi là

dataframe-> step1-> step2-> step3->result

raster-> multipliedRast-> meanRastF-> sqrtRast->resultRast

Tôi làm việc với các đối tượng tạm thời mà tôi gọi temp.

dataframe-> temp-> temp-> temp->result

Mà để lại cho tôi với các tập tin trung gian ít hơn và tổng quan hơn.

raster  <- raster('file.tif')
temp <- raster * 10
temp <- mean(temp)
resultRast <- sqrt(temp)

Để tiết kiệm thêm bộ nhớ, tôi chỉ cần xóa tempkhi không cần thiết nữa.

rm(temp)

Nếu tôi cần một số tập tin trung gian, tôi sử dụng temp1, temp2, temp3.

Để thử nghiệm tôi sử dụng test, test2...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.