1 Giới thiệu

Optimize code thường không phải ưu tiên hàng đầu đối với người dùng R. Vì bản thân R không phải là ngôn ngữ ưu tiên cho tốc độ xử lý.

Hơn nữa, nếu có máy khỏe, thì người dùng thường không quan tâm tới việc tối ưu code lắm. Nhưng với những mày cùi hoặc làm việc với dữ liệu tương đối lớn thì việc tối ưu code là việc rất cần thiết.

Để tối ưu được code thì coder phải xác định được đâu làm điểm nghẽn (bottlenecks). May mắn là có package profvis hỗ trợ rất tốt việc này.

Package này chỉ ra đoạn code nào chạy mất nhiều thời gian. Sau đó, việc còn lại là optimize, bằng cách viết lại code hoặc dùng các functions tương tự

2 Thực hành

Đầu tiên cứ lên google search package profvis trước rồi làm theo hướng dẫn

https://www.r-bloggers.com/optimising-your-r-code-a-guided-example/ https://rstudio.github.io/profvis/examples.html

library(profvis)
# Generate data
times <- 4e5
cols <- 150
data <- as.data.frame(x = matrix(rnorm(times * cols, mean = 5), ncol = cols))
data <- cbind(id = paste0("g", seq_len(times)), data)

profvis({
  data1 <- data   # Store in another variable for this run

  # Get column means
  means <- apply(data1[, names(data1) != "id"], 2, mean)

  # Subtract mean from each column
  for (i in seq_along(means)) {
    data1[, names(data1) != "id"][, i] <- data1[, names(data1) != "id"][, i] - means[i]
  }
})

Đầu tiên, data.frame có 151 dòng, 1 cột là id và 150 cột là numeric variables, đoạn code kia đơn giản là tính giá trị trung bình của 150 cột numeric, và centering từng cột 1

Nhìn tab flame graph nhìn ra ngay thủ phạm là đoạn code nào làm chậm. Trong ví dụ trên là đoạn means <- apply(data1[, names(data1) != “id”], 2, mean) là chậm nhất.

Tiếp theo nhìn sang tab data, tiếp tục nhìn ra nguyên nhân chậm là do lệnh apply. Và hàm này gọi 2 hàm as.matrixaperm. 2 hàm này chuyển data.frame thành matrix và transpose chúng.

Có 2 vấn đề đó nên chúng ta sẽ xử lý bằng cách chuyển data.frame thành matrix trước và transpose (nhưng chú ý nếu transpose làm cho mất ID). Thay vì dùng hàm apply ta có thể dùng hàm colMeans, hoặc dùng lapply, vapply hay map

Ví dụ:

library(purrr)
profvis({
  data1 <- data
  # Four different ways of getting column means
  means <- apply(data1[, names(data1) != "id"], 2, mean)
  means <- colMeans(data1[, names(data1) != "id"])
  means <- lapply(data1[, names(data1) != "id"], mean)
  means <- vapply(data1[, names(data1) != "id"], mean, numeric(1))
  means <- map(data1[, names(data1) != "id"], mean)
})

Nhìn đồ thị thấy hàm lapply và map là tốt nhất, vì vậy có thể chọn 1 trong 2 hàm này để thay thế vào đoạn code

LS0tDQp0aXRsZTogIlVzaW5nIHByb2Z2aXMgdG8gb3B0aW1pemUgY29kZSINCmF1dGhvcjogIk5ndXnhu4VuIE5n4buNYyBCw6xuaCINCmRhdGU6ICIyMSBKVU4gMjAxOSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIEdp4bubaSB0aGnhu4d1DQoNCk9wdGltaXplIGNvZGUgdGjGsOG7nW5nIGtow7RuZyBwaOG6o2kgxrB1IHRpw6puIGjDoG5nIMSR4bqndSDEkeG7kWkgduG7m2kgbmfGsOG7nWkgZMO5bmcgUi4gVsOsIGLhuqNuIHRow6JuIFIga2jDtG5nIHBo4bqjaSBsw6AgbmfDtG4gbmfhu68gxrB1IHRpw6puIGNobyB04buRYyDEkeG7mSB44butIGzDvS4NCg0KSMahbiBu4buvYSwgbuG6v3UgY8OzIG3DoXkga2jhu49lLCB0aMOsIG5nxrDhu51pIGTDuW5nIHRoxrDhu51uZyBraMO0bmcgcXVhbiB0w6JtIHThu5tpIHZp4buHYyB04buRaSDGsHUgY29kZSBs4bqvbS4gTmjGsG5nIHbhu5tpIG5o4buvbmcgbcOgeSBjw7lpIGhv4bq3YyBsw6BtIHZp4buHYyB24bubaSBk4buvIGxp4buHdSB0xrDGoW5nIMSR4buRaSBs4bubbiB0aMOsIHZp4buHYyB04buRaSDGsHUgY29kZSBsw6Agdmnhu4djIHLhuqV0IGPhuqduIHRoaeG6v3QuDQoNCsSQ4buDIHThu5FpIMawdSDEkcaw4bujYyBjb2RlIHRow6wgY29kZXIgcGjhuqNpIHjDoWMgxJHhu4tuaCDEkcaw4bujYyDEkcOidSBsw6BtIMSRaeG7g20gbmdo4bq9biAoKipib3R0bGVuZWNrcyoqKS4gTWF5IG3huq9uIGzDoCBjw7MgcGFja2FnZSBwcm9mdmlzIGjhu5cgdHLhu6MgcuG6pXQgdOG7kXQgdmnhu4djIG7DoHkuDQoNClBhY2thZ2UgbsOgeSBjaOG7iSByYSDEkW/huqFuIGNvZGUgbsOgbyBjaOG6oXkgbeG6pXQgbmhp4buBdSB0aOG7nWkgZ2lhbi4gU2F1IMSRw7MsIHZp4buHYyBjw7JuIGzhuqFpIGzDoCBvcHRpbWl6ZSwgYuG6sW5nIGPDoWNoIHZp4bq/dCBs4bqhaSBjb2RlIGhv4bq3YyBkw7luZyBjw6FjIGZ1bmN0aW9ucyB0xrDGoW5nIHThu7ENCg0KIyBUaOG7sWMgaMOgbmgNCg0KxJDhuqd1IHRpw6puIGPhu6kgbMOqbiBnb29nbGUgc2VhcmNoIHBhY2thZ2UgKipwcm9mdmlzKiogdHLGsOG7m2MgcuG7k2kgbMOgbSB0aGVvIGjGsOG7m25nIGThuqtuDQoNCmh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL29wdGltaXNpbmcteW91ci1yLWNvZGUtYS1ndWlkZWQtZXhhbXBsZS8NCmh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vcHJvZnZpcy9leGFtcGxlcy5odG1sDQoNCmBgYHtyfQ0KbGlicmFyeShwcm9mdmlzKQ0KYGBgDQoNCg0KYGBge3J9DQojIEdlbmVyYXRlIGRhdGENCnRpbWVzIDwtIDRlNQ0KY29scyA8LSAxNTANCmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh4ID0gbWF0cml4KHJub3JtKHRpbWVzICogY29scywgbWVhbiA9IDUpLCBuY29sID0gY29scykpDQpkYXRhIDwtIGNiaW5kKGlkID0gcGFzdGUwKCJnIiwgc2VxX2xlbih0aW1lcykpLCBkYXRhKQ0KDQpwcm9mdmlzKHsNCiAgZGF0YTEgPC0gZGF0YSAgICMgU3RvcmUgaW4gYW5vdGhlciB2YXJpYWJsZSBmb3IgdGhpcyBydW4NCg0KICAjIEdldCBjb2x1bW4gbWVhbnMNCiAgbWVhbnMgPC0gYXBwbHkoZGF0YTFbLCBuYW1lcyhkYXRhMSkgIT0gImlkIl0sIDIsIG1lYW4pDQoNCiAgIyBTdWJ0cmFjdCBtZWFuIGZyb20gZWFjaCBjb2x1bW4NCiAgZm9yIChpIGluIHNlcV9hbG9uZyhtZWFucykpIHsNCiAgICBkYXRhMVssIG5hbWVzKGRhdGExKSAhPSAiaWQiXVssIGldIDwtIGRhdGExWywgbmFtZXMoZGF0YTEpICE9ICJpZCJdWywgaV0gLSBtZWFuc1tpXQ0KICB9DQp9KQ0KYGBgDQoNCsSQ4bqndSB0acOqbiwgZGF0YS5mcmFtZSBjw7MgMTUxIGTDsm5nLCAxIGPhu5l0IGzDoCBpZCB2w6AgMTUwIGPhu5l0IGzDoCBudW1lcmljIHZhcmlhYmxlcywgxJFv4bqhbiBjb2RlIGtpYSDEkcahbiBnaeG6o24gbMOgIHTDrW5oIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggY+G7p2EgMTUwIGPhu5l0IG51bWVyaWMsIHbDoCBjZW50ZXJpbmcgdOG7q25nIGPhu5l0IDEgDQoNCk5ow6xuIHRhYiBmbGFtZSBncmFwaCBuaMOsbiByYSBuZ2F5IHRo4bunIHBo4bqhbSBsw6AgxJFv4bqhbiBjb2RlIG7DoG8gbMOgbSBjaOG6rW0uIFRyb25nIHbDrSBk4bulIHRyw6puIGzDoCDEkW/huqFuICoqbWVhbnMgPC0gYXBwbHkoZGF0YTFbLCBuYW1lcyhkYXRhMSkgIT0gImlkIl0sIDIsIG1lYW4pKiogbMOgIGNo4bqtbSBuaOG6pXQuDQoNClRp4bq/cCB0aGVvIG5ow6xuIHNhbmcgdGFiIGRhdGEsIHRp4bq/cCB04bulYyBuaMOsbiByYSBuZ3V5w6puIG5ow6JuIGNo4bqtbSBsw6AgZG8gbOG7h25oICoqYXBwbHkqKi4gVsOgIGjDoG0gbsOgeSBn4buNaSAyIGjDoG0gKmFzLm1hdHJpeCogdsOgICphcGVybSouIDIgaMOgbSBuw6B5IGNodXnhu4NuIGRhdGEuZnJhbWUgdGjDoG5oIG1hdHJpeCB2w6AgdHJhbnNwb3NlIGNow7puZy4gDQoNCkPDsyAyIHbhuqVuIMSR4buBIMSRw7MgbsOqbiBjaMO6bmcgdGEgc+G6vSB44butIGzDvSBi4bqxbmcgY8OhY2ggY2h1eeG7g24gZGF0YS5mcmFtZSB0aMOgbmggbWF0cml4IHRyxrDhu5tjIHbDoCB0cmFuc3Bvc2UgKG5oxrBuZyBjaMO6IMO9IG7hur91IHRyYW5zcG9zZSBsw6BtIGNobyBt4bqldCBJRCkuIFRoYXkgdsOsIGTDuW5nIGjDoG0gYXBwbHkgdGEgY8OzIHRo4buDIGTDuW5nIGjDoG0gY29sTWVhbnMsIGhv4bq3YyBkw7luZyBsYXBwbHksIHZhcHBseSBoYXkgbWFwIA0KDQpWw60gZOG7pToNCg0KYGBge3J9DQpsaWJyYXJ5KHB1cnJyKQ0KYGBgDQoNCg0KYGBge3J9DQpwcm9mdmlzKHsNCiAgZGF0YTEgPC0gZGF0YQ0KICAjIEZvdXIgZGlmZmVyZW50IHdheXMgb2YgZ2V0dGluZyBjb2x1bW4gbWVhbnMNCiAgbWVhbnMgPC0gYXBwbHkoZGF0YTFbLCBuYW1lcyhkYXRhMSkgIT0gImlkIl0sIDIsIG1lYW4pDQogIG1lYW5zIDwtIGNvbE1lYW5zKGRhdGExWywgbmFtZXMoZGF0YTEpICE9ICJpZCJdKQ0KICBtZWFucyA8LSBsYXBwbHkoZGF0YTFbLCBuYW1lcyhkYXRhMSkgIT0gImlkIl0sIG1lYW4pDQogIG1lYW5zIDwtIHZhcHBseShkYXRhMVssIG5hbWVzKGRhdGExKSAhPSAiaWQiXSwgbWVhbiwgbnVtZXJpYygxKSkNCiAgbWVhbnMgPC0gbWFwKGRhdGExWywgbmFtZXMoZGF0YTEpICE9ICJpZCJdLCBtZWFuKQ0KfSkNCmBgYA0KDQoNCk5ow6xuIMSR4buTIHRo4buLIHRo4bqleSBow6BtIGxhcHBseSB2w6AgbWFwIGzDoCB04buRdCBuaOG6pXQsIHbDrCB24bqteSBjw7MgdGjhu4MgY2jhu41uIDEgdHJvbmcgMiBow6BtIG7DoHkgxJHhu4MgdGhheSB0aOG6vyB2w6BvIMSRb+G6oW4gY29kZQ0KDQoNCg==