Bài giảng Kỹ thuật lập trình - Chương 4: Kỹ thuật viết mã nguồn hiệu quả

Chương 4:  
Kỹ thuật viết mã nguồn  
hiệu quả  
3/24/2020  
1
Nội dung  
1. Các kỹ thuật viết mã nguồn hiệu quả  
2. Những nguyên tắc cơ bản trong việc  
tăng hiệu quả viết mã nguồn  
3. Tối ưu hóa mã nguồn C/C++  
3/24/2020  
2
Chương trì nh hiệu quả  
• Trước hết là giải thuật  
• Hã y dùng giải thuật hay nhất có thể  
• Sau đó hã y nghĩ tới việc tăng tí nh hiệu quả của code  
• Ví dụ: Tí nh tổng của n số tự nhiên liên tiếp kể từ m  
void main(){  
void main(){  
long n, m, i, sum;  
cin << n;  
long n, m, sum;  
cin << n;  
cin << m;  
cin << m;  
sum =0;  
sum = (m + m+ n) * n / 2;  
cout << “Sum = “ << sum;  
}
for(i = m ; i < = m+n; i++)  
sum += i;  
cout << “Sum = “ <<sum;  
}
3/24/2020  
3
Dùng chỉ thị chương trình dịch  
• Một số compilers có vai trò rất lớn trong việc  
tối ưu chương trì nh  
• Chúng phâ n tí ch sâ u mã nguồn và làm mọi điều  
“machinely” có thể  
• Ví dụ GNU g++ compiler trên Linux/Cygwin cho  
chương trì nh viết bằng C  
g++ –O5 –o myprog myprog.c  
• Có thể cải thiện hiệu năng từ 10% đến 300%  
3/24/2020  
4
Nhưng...  
• Bạn vẫn có thể thực hiện những cải tiến mà trì nh dịch  
khô ng thể  
• Bạn phải loại bỏ tất cả những chỗ bất hợp lý trong  
code  
• Làm cho chương trì nh hiệu quả nhất có thể  
• Có thể phải xem lại khi thấy chương trì nh chạy chậm  
• Vậy cần tập trung vào đâu để cải tiến nhanh nhất, tốt nhất?  
3/24/2020  
5
Viết chương trì nh hiệu quả  
• Xá c định nguồn gâ y ké m hiệu quả  
• Dư thừa tí nh toá n - redundant computation  
• Chủ yếu  
• Trong cá c procedure  
• Cá c vò ng lặp: Loops  
3/24/2020  
6
Khởi tạo 1 lần, dùng nhiều lần  
• Before  
float f(){  
double value = sin(0.25);  
//  
…
}
• After  
double defaultValue = sin(0.25);  
float f(){  
double value = defaultValue;  
//  
…
}
3/24/2020  
7
Hàm nội tuyến (inline functions)  
Điều gì xảy ra khi một hàm được gọi?  
CPU sẽ lưu địa chỉ bộ nhớ của dòng lệnh hiện tại mà  
nó đang thực thi (để biết nơi sẽ quay lại sau lời gọi  
hàm), sao chép các đối số của hàm trên ngăn xếp  
(stack) và cuối cùng chuyển hướng điều khiển sang  
hàm đã chỉ định. CPU sau đó thực thi mã bên trong  
hàm, lưu trữ giá trị trả về của hàm trong một vùng  
nhớ/thanh ghi và trả lại quyền điều khiển cho vị trí lời gọi  
hàm  
➔ Điều này sẽ tạo ra một lượng chi phí hoạt động nhất  
định (overhead) so với việc thực thi mã trực tiếp (không  
sử dụng hàm).  
3/24/2020  
8
Hàm nội tuyến (inline functions)  
• Đối với các hàm lớn hoặc các tác vụ phức tạp, tổng  
chi phí overhead của lệnh gọi hàm thường không đáng  
kể so với lượng thời gian mà hàm mất để chạy.  
• Tuy nhiên, đối với các hàm nhỏ, thường xuyên được  
sử dụng, thời gian cần thiết để thực hiện lệnh gọi hàm  
thường nhiều hơn rất nhiều so với thời gian cần thiết  
để thực thi mã của hàm.  
• Inline functions (hàm nội tuyến) là một loại hàm  
trong ngôn ngữ lập trình C++. Từ khoá inline được sử  
dụng để đề nghị (không phải là bắt buộc) compiler  
(trình biên dịch) thực hiện inline expansion (khai  
triển nội tuyến) với hàm đó hay nói cách khác là chèn  
code của hàm đó tại địa chỉ mà nó được gọi.  
3/24/2020  
9
Hàm nội tuyến (inline functions)  
• Đối với các hàm lớn hoặc các tác vụ phức tạp, tổng  
chi phí overhead của lệnh gọi hàm thường không đáng  
kể so với lượng thời gian mà hàm mất để chạy.  
• Tuy nhiên, đối với các hàm nhỏ, thường xuyên được  
sử dụng, thời gian cần thiết để thực hiện lệnh gọi hàm  
thường nhiều hơn rất nhiều so với thời gian cần thiết  
để thực thi mã của hàm.  
• Inline functions (hàm nội tuyến) là một loại hàm  
trong ngôn ngữ lập trình C++. Từ khoá inline được sử  
dụng để đề nghị (không phải là bắt buộc) compiler  
(trình biên dịch) thực hiện inline expansion (khai  
triển nội tuyến) với hàm đó hay nói cách khác là chèn  
code của hàm đó tại địa chỉ mà nó được gọi.  
3/24/2020  
10  
Inline functions  
#include <iostream>  
#include <cmath>  
using namespace std;  
inline double hypothenuse (double a, double b){  
return sqrt (a * a + b * b);  
}
int main (){  
double k = 6, m = 9;  
// 2 dòng sau thực hiện như nhau:  
cout << hypothenuse (k, m) << endl;  
cout << sqrt (k * k + m * m) << endl;  
return 0;  
}
3/24/2020  
11  
Inline functions  
#include <iostream>  
using namespace std;  
inline int max(int a, int b){  
return a > b ? a : b;  
}
int main() {  
cout << max(3, 6) << '\n';  
cout << max(6, 3) << '\n';  
return 0;  
}
Khi chương trình trên được biên dịch, mã máy được tạo ra  
tương tự như hàm main() bên dưới:  
int main(){  
cout << (3 > 6 ? 3 : 6) << '\n';  
cout << (6 > 3 ? 6 : 3) << '\n';  
return 0;  
}
3/24/2020  
12  
Inline functions  
#include <iostream>  
using namespace std;  
inline int max(int a, int b){  
return a > b ? a : b;  
}
int main() {  
cout << max(3, 6) << '\n';  
cout << max(6, 3) << '\n';  
return 0;  
}
Khi chương trình trên được biên dịch, mã máy được tạo ra  
tương tự như hàm main() bên dưới:  
int main(){  
cout << (3 > 6 ? 3 : 6) << '\n';  
cout << (6 > 3 ? 6 : 3) << '\n';  
return 0;  
}
3/24/2020  
13  
Inline functions  
Trình biên dịch có thể không thực hiện nội  
tuyến trong các trường hợp như:  
• Hàm chứa vòng lặp (for, while, do-while).  
• Hàm chứa các biến tĩnh.  
• Hàm đệ quy.  
• Hàm chứa câu lệnh switch hoặc goto.  
3/24/2020  
14  
Inline functions  
• Ưu điểm:  
‒ Tiết kiệm chi phí gọi hàm.  
‒ Tiết kiệm chi phí của các biến trên ngăn xếp khi hàm được  
gọi.  
‒ Tiết kiệm chi phí cuộc gọi trả về từ một hàm.  
• Nhược điểm:  
‒ Tăng kích thước file thực thi do sự trùng lặp của cùng một  
mã .  
‒ Khi được sử dụng trong file tiêu đề (*.h), nó làm cho file tiêu  
đề của bạn lớn hơn.  
‒ Hàm nội tuyến có thể không hữu ích cho nhiều hệ thống  
nhúng. Vì trong các hệ thống nhúng, kích thước mã quan  
trọng hơn tốc độ.  
3/24/2020  
15  
Macros  
#define max(a,b) (a > b ? a : b)  
• Cá c hàm inline cũng giống như macros vì cả 2 được  
khai triển khi dịch compile time  
• macros được khai triển bởi preprocessor, cò n inline  
functions được truyền bởi compiler.  
• Tuy nhiên có nhiều điểm khá c biệt:  
• Inline functions tuâ n thủ cá c thủ tục như 1 hàm binh  
thường.  
• Inline functions có cùng syntax như cá c hàm khá c, chỉ  
có điều là có thêm từ khó a inline khi khai bá o hàm.  
• Cá c biểu thức truyền như là đối số cho inline functions  
được tí nh 1 lần. Biểu thức truyền như tham số cho  
macros có thể được tí nh mỗi lần macro được sử dụng.  
• Bạn khô ng thể gỡ rối cho macros, nhưng với inline  
functions thì có thể.  
3/24/2020  
16  
Biến tĩnh (static variables)  
• Kiểu dữ liệu static tham chiếu tới global hay 'static'  
variables, chúng được cấp phá t bộ nhớ khi dịch  
compile-time.  
int int_array[100];  
int main() {  
static float float_array[100];  
double double_array[100];  
char *pchar;  
pchar = (char *)malloc(100);  
/* .... */  
return (0);  
}
3/24/2020  
17  
Static Variables  
• Cá c biến khai bá o trong chương trì nh con được cấp phá t bộ  
nhớ khi chương trì nh con được gọi và sẽ bị loại bỏ khi kết  
thúc chương trì nh con.  
• Khi bạn gọi lại chương trì nh con, cá c biến cục bộ lại được  
cấp phá t và khởi tạo lại.  
• Nếu bạn muốn 1 giá trị vẫn được lưu lại cho đến khi kết  
thúc toàn chương trì nh, bạn cần khai bá o biến cục bộ của  
chương trì nh con đó là static và khởi tạo cho nó 1 giá trị.  
• Việc khởi tạo sẽ chỉ thực hiện lần đàu tiên chương trì nh được  
gọi và giá trị sau khi biến đổi sẽ được lưu cho cá c lần gọi sau.  
• Bằng cá ch này 1 chương trì nh con có thể “nhớ” một vài mẩu  
tin sau mỗi lần được gọi.  
• Dùng biến Static thay vì Global:  
• Ưu điểm của 1 biến static: biến cục bộ của chương trì nh con,  
do đó trá nh được cá c hiệu ứng phụ (side effects).  
3/24/2020  
18  
Stack, heap  
• Khi thực hiện, vùng dữ liệu data segment của  
một chương trì nh được chia làm 3 phần:  
• static, stack, và heap data.  
• Static: global hay static variables  
• Stack data:  
• cá c biến cục bộ của chương trì nh con  
• ví dụ double_array trong ví dụ trên.  
• Heap data:  
• Dữ liệu được cấp phá t động (ví dụ, pchar trong ví  
dụ trên).  
• Dữ liệu này sẽ cò n cho đến khi ta giải phó ng hoặc  
khi kết thúc chương trì nh.  
3/24/2020  
19  
Tính toán trước các giá trị  
• Nếu bạn phải tí nh đi tí nh lại 1 biểu thức, thì  
nên tí nh trước 1 lần và lưu lại giá trị, rồi dùng  
giá trị ấy sau này  
int f(int i){  
static int[] values =  
if (i < 10 && i >= 0){  
{0, 0, 2,3*3-3, ..., 9*9-9};  
return i * i - i;  
int f(int i){  
if (i < 10 && i >= 0)  
return values[i];  
return 0;  
}
return 0;  
}
}
3/24/2020  
20  
Tải về để xem bản đầy đủ
pdf 50 trang yennguyen 22520 Free
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 4: Kỹ thuật viết mã nguồn hiệu quả", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfbai_giang_ky_thuat_lap_trinh_chuong_4_ky_thuat_viet_ma_nguon.pdf