Search…

Tối Ưu Mã C/C++ Cho Người Mới Bắt Đầu

22/09/20204 min read
Giới thiệu các phương pháp tối ưu hoá mã nguồn C/C++.

Tối giản hóa các biểu thức toán học

CPU xử lí các phép cộng, trừ nhanh hơn so với các phép toán nhân và chia.

Có biểu thức sau

P = (A * B + A * C);

Với biểu thức trên cần 2 phép nhân và 1 phép toán cộng, có thể đơn giản biểu thức này lại như sau

P = A * (B + C);

Đã đơn giản đi được tới 1 phép nhân.

Ngoài ra có một đoạn inline assembly để các bạn tham khảo thêm (không cần thiết phải quan tâm sâu vào code bên dưới, chỉ đề cập thêm) __asm{}

int A = 1;
int B = 2;
int C = 3;
int P = 0;
	
__asm
{
	MOV	EAX, B
	ADD	EAX, C
	IMUL	EAX, A
	MOV	DWORD ptr [P], EAX
}

Chọn kiểu dữ liệu để thao tác

Nếu biểu thức số nguyên có dạng A = 2n thì nên tận dụng phép dịch bit như sau A << n,  và ngược lại với dạng A = 1/2n hoặc A = 2-n thì dùng A >> n.

Tốc độ xử lí kiểu dữ liệu tuần tự là int > float > double. Có thể hiểu rằng độ chính xác càng cao thì cần phải có một vùng nhớ tương ứng cùng với những phép toán thao tác trên đó. Và lưu ý dùng thao tác dịch bit (shift left logical, shift right logical) thay cho nhân chia số nguyên khi có dạng A * 2±n.

Hạn chế sử dụng biến tạm, thanh ghi tạm

Có đoạn chương trình sau

int main()
{
	int A = 1;
	int B = 2;
	int C = A + B;

	return 0;
}

Trong ba dòng codes đâu tiên trong hàm main, các giá trị được gán lần lượt cho A và B, và trong phép gán C xảy ra việc dùng thanh ghi tạm EAX lưu kết quả trung gian, và sau khi thực hiện và kết quả cuối cùng trả về C.

Với đoạn code trên, ta vẫn có thể đơn hơn dưới đoạn code sau:

int main()
{
	int A = 1;
	int C = A + 2;
	return 0;
}

Và thậm chí cũng có thể viết C = 1 + 2 để giảm tối đa phải sử dụng biến tạm, thanh ghi tạm. Do đó, việc hạn chế sử dụng biến cũng đồng nghĩa với việc hạn chế sử dụng thanh ghi tạm.

Sử dụng truyền tham chiếu

Trong quá trình bắt đầu tiếp cận lập trình, có một hàm quen thuộc như sau

void swap(int & a,int & b)
{
	int temp;
	temp = a;
	a = b;
	b = temp; 
}

Nhận thấy rằng phải mất một biến khác làm trung gian lưu giữ giá trị của biến a hoặc b. Nhưng có một cách viết sau đây vừa tiết kiệm được biến temp và giúp chương trình chạy nhanh hơn

void swap(int & a, int & b)
{
	a += b;
	b = a - b;
	a = a - b;
}

Toán tử XOR ( ^ ) là toán tử thao tác trên bit nên sẽ thực hiện nhanh hơn. Và cách thứ ba sau đây sẽ nhanh hơn hai cách ở trên

void swap(int & a,int & b)
{
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

Có thể tự chứng minh bài toán phía trên, lưu ý cần có thêm hiểu biết về các phép toán trên bit để có thể chứng minh.

Thông qua hàm swap, bài viết cũng đề cập thêm việc sử dụng phương thức truyền tham số cho hàm. Có hai cách truyền tham số cho hàm: “truyền tham trị” và “truyền tham chiếu”. Với truyền tham trị thì “bản sao” của đối số thực được truyền vào, mọi thao tác thay đổi dữ liệu sẽ trên bản sao đó, và không làm thay đổi được giá trị đối số thực. Với truyền tham chiếu thì “địa chỉ thực” của đối số thực sự được truyền vào hàm, và đôi khi gây ra những trường hợp ngoài mong đợi.

Từ khóa "inline"

Ngoài ra, bản thân hàm cũng là một địa chỉ và và khi lời gọi hàm sẽ diễn ra cơ chế push-pop trong stack, cho đến khi nào các tham số của hàm được giải phóng thì sẽ tiếp tục các dòng lệnh trong hàm main. Đối với những hàm có cấu trúc lớn thì việc truy xuất ra và vào không là gì cả, nhưng với hàm chỉ có vài dòng câu lệnh đơn giản thì nên dùng từ khóa inline. Cũng có nghĩa là nó sẽ thay thế lời gọi hàm đấy bằng những dòng lệnh đó vào trong hàm main và làm giảm thời gian truy xuất.

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024