Tính toán hiệu năng cao với SAS

Viết code tối ưu là kỹ năng cần thiết khi học một ngôn ngữ mới. Việc viết code tối ưu nhằm tăng khả năng xử lý của chương trình, từ đó giúp ta tiết kiệm được thời gian phân tích dữ liệu. Điều này đặc biệt quan trọng với các thuật toán phức tạp với số lượng các trường hợp cần thử lên đến hàng triệu, hàng tỷ.

Giới thiệu

Trong mục này, tôi sẽ đưa ra một số ví dụ về cách viết code không tối ưu trong SAS. Từ đó, ta sẽ tìm cách giải quyết các vấn đề này trong các mục phía dưới.

Data step

Với ví dụ về Cross validation, bài toán đặt ra là: làm thế nào để tạo ra 1000 dữ liệu mới, mỗi dữ liệu có train và validate chiếm 70% và 30% số lượng quan sát ban đầu (và thêm cột ID). Ý tưởng đơn giản nhất có thể nghĩ ra như sau:

  • Chia dữ liệu đã cho theo tỷ lệ 70/30 bằng một hàm random.
  • Thực hiện bước ở trên 1000 lần, mỗi lần nối dữ liệu vừa chia vào một dữ liệu tổng hợp

Ta thực hiện trên code SAS như sau:

%MACRO BOOSTRAP;
	%DO I=1 %TO 1000;
		%IF &I=1 %THEN
			%DO;

				DATA OUTPUT;
					SET SASHELP.CARS;
					ID=&I;

					IF RANUNI(ID)<0.70 THEN
						ROLE="T";
					ELSE ROLE="V";
					OUTPUT;
				RUN;

			%END;
		%ELSE
			%DO;

				DATA TEMP00;
					SET SASHELP.CARS;
					ID=&I;

					IF RANUNI(ID)<0.70 THEN
						ROLE="T";
					ELSE ROLE="V";
					OUTPUT;
				RUN;

				DATA OUTPUT;
					SET OUTPUT TEMP00;
				RUN;

			%END;
	%END;
%MEND;

%BOOSTRAP;

Thời gian chạy: 46.609s. Với dữ liệu nhẹ như SASHELP.CARS, thời gian chạy trên là quá lâu.

Variable Aggregation

Với dữ liệu OUTPUT bên trên, giả sử ta muốn tính giá trị MSRP trung bình theo từng biến Make, Type, Origin, DriveTrain. Ta thử tiếp dụng dùng Loop như sau:

Hồi quy

Tính toán hiệu năng cao (cơ bản)

Sơ đồ sau đây minh họa cách thức SAS xử lý dữ liệu:

SAS lưu trữ dữ liệu trên ổ cứng và đọc dữ liệu vào RAM khi có yêu cầu xử lý. Với quy trình trên, công đoạn tốn nhiều thời gian nhất chính là đọc dữ liệu SAS từ đĩa (disk) vào RAM. Do đó, để tăng tốc độ xử lý của SAS, ta nên thực hiện các bước như sau:

Hạn chế các bước thừa

Các bước tính toán trên cùng một dữ liệu nên được gôp lại thành một lần Datastep. Ví dụ không nên viết:

Dont

DATA VIETNAM;
	SET ALL_COUNTRY;
	WHERE COUNTRY="Viet Nam";
RUN;
DATA FRANCE;
	SET ALL_COUNTRY;
	WHERE COUNTRY="France";
RUN;

Mà nên viết là;

Do

DATA VIETNAM FRANCE;
	SET ALL_COUNTRY;
	IF WHERE COUNTRY="France" THEN OUTPUT FRANCE;
	ELSE IF COUNTRY="Viet Nam" THEN OUTPUT VIETNAM;
RUN;

Hoặc không nên viết:

Dont

DATA OUTPUT;
	SET INPUT;
	SUM_AB=A+B;
RUN;

DATA OUTPUT;
	SET INPUT;
	SUM_ABC=SUM_AB+C;
RUN;

Mà hãy viết lại thành:

Do

DATA OUTPUT;
	SET INPUT;
	SUM_AB=A+B;
	SUM_ABC=SUM_AB+C;
RUN;

Sử dụng If-else thay cho If

Nguyên tắc của việc này là khi viết if, chương trình phải quét toàn bộ các quan sát trong bảng. Ngược lại khi viết else-if thì chương trình chỉ cần kiểm tra các quan sát “còn lại” sau lần if trước. Ví dụ không nên viết:

Dont

DATA OUTPUT;
	SET INPUT;
	IF COUNTRY="Viet Nam" THEN CAPITAL="Ha Noi";
	IF COUNTRY="France" THEN CAPITAL="Paris";
	IF COUNTRY="England" THEN CAPITAL="London";
RUN;

Mà nên viết là:

Do

DATA OUTPUT;
	SET INPUT;
	IF COUNTRY="Viet Nam" THEN CAPITAL="Ha Noi";
	ELSE IF COUNTRY="France" THEN CAPITAL="Paris";
	ELSE CAPITAL="London";
RUN;

Chú ý độ dài của biến character

Một số dữ liệu khi import từ SQL sẽ gặp tình trạng biến Character có độ dài quá lớn. Nguyên nhân thường là các cột này được để định dạng nvarchar(max) trong khi tạo ở SQL. Với trình trạng này, ta thường thực hiện một số phương pháp như sau:

Sử dụng độ dài hợp lý

DATA OUTPUT;
	SET INPUT;
	LENGTH NEW_VAR $20.;
	NEWVAR=SUBSTR(OLD_VAR, 1, 20);
RUN;

Sử dụng macro để giảm bớt kích thước của dữ liệu, ví dụ macro Data Reduce Size

Hạn chế các dữ liệu thừa

Việc hạn chế các dữ liệu thừa sẽ làm giảm dung lượng dữ liệu, từ đó khiến chương trình chạy nhanh hơn. Trong cú pháp của DATA STEP thì tất cả dữ liệu ở mệnh đề SET sẽ được đọc vào RAM. Do đó, một số thủ thuật nên thực hiện như sau:

Chỉ giữ các biến cần thiết khi đọc dữ liệu

DATA OUTPUT;
	SET INPUT (KEEP= A B C);
	/********OTHER CODE---------*/
RUN;

Chỉ đọc phần dữ liệu cần thiết

DATA OUTPUT;
	SET INPUT (WHERE=(COUNTRY="Viet Nam"));
	/********OTHER CODE---------*/
RUN;

Xóa các dữ liệu không cần thiết

PROC DELETE DATA=DATA_A DATA_B DATA_C;
RUN;

hoặc

PROC DATASETS LIBRARY=WORK NOPRINT;
	DELETE TEMP:;
RUN;

Câu lệnh trên sẽ xóa tất cả các data có tên bắt đầu bằng TEMP. Do đó thủ thuật khi viết code là đặt tên các bẳng tạm bắt đầu bằng TEMP ví dụ TEMP00, TEMP01, …

Sử dụng procedure một cách hiệu quả

Các procedure đã được SAS tối ưu cho việc xử lý dữ liệu. Do đó, nên sử dụng các Procedure ngay khi có thể. Ví trong minh họa ở phần trên, sử dụng Proc Append sẽ cho kết quả nhanh hơn set data. Ví dụ: Nếu thay

DATA OUTPUT;
	SET OUTPUT TEMP00;
RUN;

bằng code

PROC APPEND BASE=OUTPUT DATA=TEMP00; RUN;

Thì thời gian chạy macro BOOSTRAP giảm xuống còn 14.439s, nghĩa là nhanh gấp 3 lần.

Ngoài ra, SAS có sẵn các procedure tính toán hiệu năng cao (bắt đầu bằng HP - high performance). Luôn sử dụng các procedure này khi có thể. Bảng dưới đây liệt kê một số procedure tính toán hiệu năng cao:

Data Preparation Data Exploration/ Transform Analysis and Modeling
HPSAMPLE HPSUMMARY HPLOGISTIC
  HPBIN HPSPLIT
  HPCORR HPREG

Lưu ý rằng các chương trình này có thể không có sẵn trên SAS phiên phản University.

Tính toán hiệu năng cao (nâng cao)

Cấu hình SAS

Để tăng tốc độ xử lý của SAS, ta có thể tùy chỉnh một số tham số như sau:

Điều chỉnh số lượng CPU là số lượng sô nhân (core) CPU sẽ tham gia vào thực thi câu lệnh. Các máy tính đời mới thường trang bị CPU với 4/8/16 nhân. Ta dùng lệnh:

OPTIONS CPUCOUNT=16;

Điều chỉnh RAM tối đa có thể dùng là dung lượng RAM tối đa mà SAS có thể sử dụng. Mặc định SAS có thể sử dụng 2GB RAM. Để điều chỉnh, ta tìm file config của SAS tại thư mục:

C:\Program Files\SASHome\SASFoundation\9.4\nls\en\

Sau đó mở file sasv9.cfg và sửa tham số sau: -MEMSIZE 2G thành -MEMSIZE 8G nghĩa là SAS có thể sử dụng tối đa 8GB RAM.

Sử dụng ổ cứng SSD: Như ta đã biết, SAS có thể dữ lý dữ liệu trên ổ cứng. Do đó, tốc độ ổ cứng càng cao thì SAS xử lý càng nhanh.

Điều chỉnh thư viện WORK: thư viện WORK là thư viện tạm của SAS (xem thêm SAS Enterprise Guide). Thư việc này được đặt mặc định tại thư mục TEMP ở ổ C:. Để thay đổi (ví dụ muốn đặt thư viện này ở ổ cứng SSD là ổ D:) ta mở file sasv9.cfg và thay đổi -WORK "path" trong đó path là đường dẫn đến thư mục cần chuyển. Ví dụ -WORK "E:\SASWORKSPACE\WORK"

Đừng LOOP bằng Macro

Khi phân tích Macro Boostrap phía trên, ta thấy sơ đồ như sau:

Dễ dàng thấy rằng, số lần đọc và ghi dữ liệu quá lớn. Thay vào đó, ta sử dụng loop trong datastep như sau:

DATA OUTPUT;
	DO ID = 1 TO 1000;
		DO J = 1 TO N;
			SET SASHELP.CARS NOBS=N POINT=J;

			IF RANUNI(ID)<0.70 THEN
				ROLE="T";
			ELSE ROLE="V";
			OUTPUT;
		END;
	END;
	STOP;
RUN;

Thời gian thực thi câu lệnh là 0.534s. Trong ví dụ trên, ta cần lưu ý một số vấn để sau:

  • DO J = 1 TO N; SET SASHELP.CARS NOBS=N POINT=J; câu lệnh này sẽ loop qua tất cả các quan sát của dữ liệu SASHELP.CARS. Trong đó J là phần tử loop còn N là số cố định thể hiện tổng số quan sát trong dữ liệu SASHELP.CARS. Từ dưới mục này ta có thể thực hiện tính toán bất kỳ trên mỗi quan sát của SASHELP.CARS.
  • STOP; là cú phát bắt buộc khi muốn loop qua dữ liệu bằng phương pháp trên. Nếu không có STOP thì câu lệnh sẽ chạy không bao giờ dừng.

Để tăng tính hiệu quả hơn nữa, ta có thể load dataset vào RAM bằng cú pháp đầy đủ như sau:

SASFILE SASHELP.CARS  LOAD;

DATA OUTPUT;
	DO ID = 1 TO 1000;
		DO J = 1 TO N;
			SET SASHELP.CARS NOBS=N POINT=J;

			IF RANUNI(ID)<0.70 THEN
				ROLE="T";
			ELSE ROLE="V";
			OUTPUT;
		END;
	END;
	STOP;
RUN;

SASFILE SASHELP.CARS  CLOSE;

Thời gian thực thi câu lệnh là 0.293s. Chú ý rằng cú pháp SASFILE SASHELP.CARS LOAD/CLOSE dùng để load và unload dữ liệu SASHELP.CARS vào RAM.

Sử dụng By statement

Tính toán hàng loạt


Getting a JavaScript console error

Musce libero nunc, dignissim quis turpis quis, semper vehicula dolor. Suspendisse ti...

Tài liệu tham khảo

Một số tài liệu tham khảo hữu ích về chương trình SAS và cách thức xây dựng Credit S...