Khi xây dựng một số model có sử dụng những dữ liệu bigdata cỡ vài GB trở lên chúng ta thường gặp phải những vấn đề trong việc load dữ liệu. Những dữ liệu lớn như vậy sẽ tiêu tốn thời gian xử lý và xác xuất bị treo máy trong quá trình import dữ liệu là rất cao. Thậm chí máy treo khi đã thực hiện được 99% sẽ vô cùng đáng tiếc. Chính vì thế phân tích được tốc độ và hiệu năng của các package load data trên R sẽ khắc phục được phần nào những sự cố đáng tiếc trên và tiết kiệm thời gian, cải thiện hiệu năng công việc cho chúng ta. Trong bài viết này tôi sẽ làm một phép so sánh tốc độ đọc dữ liệu của các phương thức quen thuộc trong R bao gồm:
Dữ liệu mà tôi sử dụng là chuỗi chứng khoán VCB có kích cỡ rất nhẹ nhằm mục đích test kết quả nhanh hơn. Dựa trên code này các bạn có thể thực hành dữ liệu với các size lớn hơn. Package được sử dụng để benchmark thời gian xử lý đọc file là microbenchmark, đây là package đưa ra những thống kê mô tả sơ bộ về min, max, median, lower quintile, upper quintile của thời gian xử lý một hàm số bất kì và cũng cho đưa ra so sánh giữa nhiều hàm số khác nhau để tìm ra hàm số tối ưu thời gian nhất. Tiến hành microbenchmark với dữ liệu là file VCB.csv
library(microbenchmark)
library(utils)
library(readr)
library(data.table)
path_csv <- "D:/Rcode/VCB.csv"
#nen chon file nhe (duoi 1000 records) vi lenh microbenchmark chay lai 100 lan de #thong ke mo ta thoi gian chay lenh
benchmark <- microbenchmark(readCSV = utils::read.csv(path_csv),
readrCSV = readr::read_csv(path_csv),
fread = data.table::fread(path_csv))
print(benchmark,signif = 2)
## Unit: milliseconds
## expr min lq mean median uq max neval
## readCSV 3.5 6.2 8.6 6.8 8.8 36 100
## readrCSV 6.3 9.2 19.0 10.0 13.0 530 100
## fread 2.7 3.8 8.5 4.0 4.6 380 100
Chúng ta có thể thấy đối với việc đọc dữ liệu là các file csv hoặc txt thì fread là lựa chọn số 1. Tốc độ của nó nhanh gâp khoảng 4 lần so với readr::read_csv() và gấp 1.5 lần so với utils::read.csv(). Kết quả này cũng chỉ là so sánh tương đối vì tốc độ chạy còn phụ thuộc vào nhiều yếu tố khác như định dạng file, cấu hình vật lý máy tính, kích thước dữ liệu và các lần chạy khác nhau cũng ra kết quả thời gian khác nhau. Tuy nhiên trong hầu hết trường hợp fread đều đưa ra kết quả tốt nhất. Tại sao fread lại có tốc độ tối ưu như vậy? Điểm mấu chốt chính là khi đọc một file fread sẽ map file trực tiếp lên RAM và sử dụng con trỏ để đọc dữ liệu thay vì sao lưu dữ liệu vào một vùng nhớ đệm (buffer) và đọc từ vùng nhớ đệm này như đa phần các phương thức thông thường khác sử dụng. Để hiển thị thời gian thực hiện từng bước chạy của fread ta sử dụng argument verbose = TRUE.
library(microbenchmark)
library(utils)
library(readr)
library(data.table)
path_csv <- "D:/Rcode/VCB.csv"
data.table::fread(path_csv, verbose = TRUE)
## Input contains no \n. Taking this to be a filename to open
## File opened, filesize is 0.000015 GB.
## Memory mapping ... ok
## Detected eol as \n only (no \r afterwards), the UNIX and Mac standard.
## Positioned on line 1 after skip or autostart
## This line is the autostart and not blank so searching up for the last non-blank ... line 1
## Detecting sep ... ','
## Detected 7 columns. Longest stretch was from line 1 to line 30
## Starting data input on line 1 (either column names or first row of data). First 10 characters: DATE,CLOSE
## All the fields on line 1 are character fields. Treating as the column names.
## Count of eol: 329 (including 1 at the end)
## Count of sep: 1968
## nrow = MIN( nsep [1968] / (ncol [7] -1), neol [329] - endblanks [1] ) = 328
## Type codes (point 0): 4343333
## Type codes: 4343333 (after applying colClasses and integer64)
## Type codes: 4343333 (after applying drop or select (if supplied)
## Allocating 7 column slots (7 - 0 dropped)
## Read 328 rows. Exactly what was estimated and allocated up front
## 0.000s ( 0%) Memory map (rerun may be quicker)
## 0.000s ( 0%) sep and header detection
## 0.000s ( 0%) Count rows (wc -l)
## 0.002s ( 67%) Column type detection (100 rows at 10 points)
## 0.000s ( 0%) Allocation of 328x7 result (xMB) in RAM
## 0.001s ( 33%) Reading data
## 0.000s ( 0%) Allocation for type bumps (if any), including gc time if triggered
## 0.000s ( 0%) Coercing data already read in type bumps (if any)
## 0.000s ( 0%) Changing na.strings to NA
## 0.003s Total
## DATE CLOSE TICKER OPEN HIGH LOW VOLUME
## 1: 27/04/2017 34.85 VCB 35.2 35.30 34.70 2240170
## 2: 26/04/2017 35.05 VCB 35.1 35.20 35.00 709260
## 3: 25/04/2017 35.10 VCB 35.1 35.30 34.95 656130
## 4: 24/04/2017 35.10 VCB 35.5 35.75 35.10 450210
## 5: 21/04/2017 35.50 VCB 35.4 35.90 35.40 517030
## ---
## 324: 08/01/2016 42.20 VCB 42.3 42.60 41.70 676340
## 325: 07/01/2016 42.50 VCB 42.8 42.90 42.00 935460
## 326: 06/01/2016 43.00 VCB 42.5 43.20 42.50 699490
## 327: 05/01/2016 42.50 VCB 42.9 43.40 42.40 1133260
## 328: 04/01/2016 43.20 VCB 43.8 44.00 42.60 761750
Tuy nhiên ngoài csv ra còn một số định dạng dữ liệu khác như rds, rdata và feather. Chúng ta cùng làm một phép so sánh để xem liệu định dạng dữ liệu nào cho phép load data nhanh nhất.
#import thu vien rio de convert dinh dang file
library(rio)
library(feather)
#setup thu muc luu tru du lieu
setwd("D:/Rcode")
#convert file sang dang rds. Mot file moi la VCB.rds duoc tao thanh
convert("VCB.csv","VCB.rds")
#tuong tu file VCB.rdata duoc tao thanh
convert("VCB.csv","VCB.rdata")
#convert sang dinh dang feather. phai import thu vien feather truoc khi convert
convert("VCB.csv","VCB.feather")
#them 1 file VCB.txt
convert("VCB.csv","VCB.txt")
#benchmark cac file, setup times = 20 de chi thong ke 20 lan cho nhanh
benchmark <- microbenchmark(readCSV = utils::read.csv("VCB.csv"),
readrCSV = readr::read_csv("VCB.csv"),
fread = data.table("VCB.csv"),
loadRdata = base::load("VCB.rdata"),
loadRDS = base::readRDS("VCB.rds"),
readFeather = feather::read_feather("VCB.feather"), times = 20
)
## Note: no visible global function definition for 'NROW'
print(benchmark)
## Unit: microseconds
## expr min lq mean median uq max
## readCSV 5730.901 5841.3390 6432.2294 6122.273 6398.0710 12508.885
## readrCSV 7957.829 8453.9090 10584.5845 9049.758 11297.0350 19546.072
## fread 533.025 767.9265 17861.5924 860.584 971.6135 341234.001
## loadRdata 785.115 834.1100 895.6510 903.257 935.2620 1061.703
## loadRDS 643.264 689.0990 859.1612 769.309 905.6285 2434.764
## readFeather 369.048 497.4630 563.9431 549.027 643.0670 841.223
## neval
## 20
## 20
## 20
## 20
## 20
## 20
Về mặt hiệu năng ta có thể thấy hàm read_feather() có tốc độ nhanh nhất khi nó gấp hơn 15 lần hàm fread có tốc độ xếp thứ 2. Đây là hàm thuộc package feather là một package chuyên xử lý các định dạng file feather. Mặc dù rất nhanh nhưng nhược điểm của hàm là chỉ đọc dữ liệu có định dạng feather là một định dạng rất hiếm khi được save từ các dữ liệu nguồn. Do vậy chúng ta phải tốn thời gian convert dữ liệu. Tuy nhiên feather lại là một dạng dữ liệu nhẹ kiểu binary format được thiết kế để sử dụng trên cả 2 ngôn ngữ python và R. Do đó nó đáp ứng được nhu cầu phân tích của nhiều đối tượng nghiên cứu hơn chứ không chuyên biệt cho R như các định dạng rdata hay rds. Ngoài ra feather là định dạng kiểu binary nên việc đọc và ghi dữ liệu lên RAM được tối giản hóa nhất khi máy tính không mất nhiều công đoạn xử lý mã hóa kí tự. Hiệu năng của việc đọc file feather còn phụ thuộc vào hiệu năng của ổ cứng, RAM. Nếu ổ cứng SSD, dung lượng RAM lớn, chip xử lý tốc độ cao thì tốc độ đọc càng nhanh. Như vậy đối với các dữ liệu lớn cỡ vài Gb trở lên thì chúng ta nên chuyển có 2 lựa chọn để đọc dữ liệu:
Tuy nhiên máy của bạn cũng phải xịn một chút thì mới đảm bảo quá trình đọc dữ liệu thành công. Nếu máy cấu hình quá yếu sẽ thường gặp sự cố tràn RAM, treo máy. Nếu máy có cấu hình cao nhưng không biết sử dụng đúng hàm tối ưu cũng có thể gặp sự cố tương tự. Vì vậy bạn phải vừa là người có kiến thức về hiệu năng của các package load data vừa phải có một chiếc máy tính cấu hình tốt để mọi việc thuận lợi.