Nguyên tắc lập trình liên quan đến hiệu quả của phần mềm (tính toán) và việc sử dụng các biến


8

Tôi là nhà tâm lý học được đào tạo bài bản, không phải là lập trình viên, vì vậy đôi khi các khía cạnh nâng cao hơn của lập trình thoát khỏi tôi, đặc biệt là về hiệu quả của chương trình và / hoặc một số thực tiễn tốt nhất, trong trường hợp này liên quan đến việc sử dụng các biến.

Đây là một số mã giả:

var a;
var b;
var c;
function GetSomeInformation() {
    returns "XYZ";
}

a = GetSomeInformation();
b = GetSomeInformation();
c = GetSomeInformation();

Vì vậy, câu hỏi của tôi là: Việc
lưu trữ dữ liệu vào một biến một lần và ít tham khảo trái ngược với các cuộc gọi lặp lại cho cùng một chức năng?

IE, mã này có hiệu quả hơn không?

var results = GetSomeInformation();
a = results;
b = results;
c = results;

Nếu vậy, mức tăng hay giảm về hiệu quả nói chung là giống nhau giữa các ngôn ngữ, hay nó có thay đổi theo ngôn ngữ không? Có ngưỡng nào để đặt tên biến trở nên tốt hơn so với sử dụng lệnh gọi hàm lặp lại hoặc ngược lại không? Những khía cạnh nào có thể thay đổi hiệu quả của nó (ví dụ, có sự khác biệt nào cho dù đó là chức năng thành viên của một lớp so với chức năng thông thường trong phạm vi toàn cầu)? Vân vân.

Nếu có thể, tôi muốn biết cụ thể cách áp dụng một khái niệm như vậy đối với các hộp thoại C ++ / MFC, khi nó xuất hiện khi tôi đang viết một số mã trong khung đó.

// define pointers to the items in my form
CListBox *pLISTBOX = (CListBox*) GetDlgItem(LISTBOX);
CStatic *pSTATIC_A = (CStatic*) GetDlgItem(STATIC_A);
CStatic *pSTATIC_B = (CStatic*) GetDlgItem(STATIC_B);
CEdit *pEDIT_BOX_A = (CEdit*) GetDlgItem(EDIT_BOX_A);
CEdit *pEDIT_BOX_B = (CEdit*) GetDlgItem(EDIT_BOX_B);
int SelectedIndex = pLISTBOX->GetCurSel();
pSTATIC_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pSTATIC_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));

Câu trả lời:


10

Để trả lời câu hỏi của bạn: Nói chung sẽ hiệu quả hơn khi lưu trữ dữ liệu trong một biến và tham chiếu đó. Các biến cục bộ bổ sung là giá rẻ.

Bắt đầu với một ví dụ đơn giản, giả sử bạn có đoạn mã:

x = 5;
y = x*x + 1;
z = x*x + 2;

Nếu bạn nhìn vào đoạn mã trên và giả vờ rằng bạn đang thực thi CPU từng bước, CPU sẽ thực hiện phép nhân hai lần với cùng một giá trị x. Điều đó hầu như luôn kém hiệu quả hơn so với thực hiện phép nhân một lần:

x = 5;
x2 = x*x;
y = x2 + 1;
z = x2 + 2;

Bây giờ, các trình biên dịch hiện đại hầu như luôn có một tối ưu hóa được gọi là loại bỏ phổ biến phụ , sẽ có tác dụng trích xuất x2giống như trong ví dụ trên. Vì vậy, bạn thường không phải lo lắng về nó, bởi vì trình biên dịch có mặt sau của bạn.

Phải nói rằng, sử dụng một biến như x2có thể làm giảm đáng kể độ phức tạp của các dòng sau, vì vậy không có gì sai khi đưa ra một biến như thế vì lý do dễ đọc.

Trong trường hợp mã MFC của bạn, bạn liên tục gọi pLISTBOX->GetItemData(SelectedIndex). Vì đây là một lệnh gọi hàm thực hiện cuộc gọi vào HĐH để lấy thêm dữ liệu, trình biên dịch không thể thực hiện loại bỏ biểu hiện phụ phổ biến trên đó. Thay vào đó, tôi sẽ giới thiệu một biến mới để bạn chỉ phải thực hiện cuộc gọi một lần:

int SelectedIndex = pLISTBOX->GetCurSel();
const char *data = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(data);
pSTATIC_B->SetWindowText(data);
pEDIT_BOX_A->SetWindowText(data);
pEDIT_BOX_B->SetWindowText(data);

1
Có thể muốn thêm rằng CSE chỉ hoạt động trong ví dụ ban đầu nếu hàm được gọi ( GetSomeInformation) là thuần túy trình biên dịch nhận thức được thực tế này. Mặt khác, trình biên dịch phải gọi nó ba lần để đảm bảo các tác dụng phụ xảy ra như mong đợi.
tdammers

1
+1 để trích dẫn trình tối ưu hóa. OP cần hiểu rằng mã nguồn chỉ là lời khuyên cho trình biên dịch về cách tạo mã đối tượng.
Ross Patterson

3
  1. Có, mã thứ hai hiệu quả hơn một chút so với mã thứ nhất, trong hầu hết các ngôn ngữ.
  2. Nó có thể không làm cho bất kỳ sự khác biệt thực tế.
  3. Nó có thể không tạo sự khác biệt trong khả năng đọc, vì vậy đi với mã sạch hơn.

Ngoài ra, một trình biên dịch tốt (bao gồm JIT của Java) sẽ lặp lại các cuộc gọi phương thức và bộ đệm được lặp lại các giá trị được sử dụng, do đó hai ví dụ có thể sẽ có hiệu suất tương đương.

Tham khảo các quy tắc sau đây:

Làm cho nó hoạt động, làm cho nó đúng, làm cho nó nhanh chóng ... theo thứ tự đó. - Kent Beck

Nguyên tắc tối ưu hóa đầu tiên : Đừng.

Nguyên tắc tối ưu hóa thứ hai : Đừng ... chưa.

Nguyên tắc tối ưu hóa thứ ba : Hồ sơ trước khi tối ưu hóa

Tối ưu hóa sớm là gốc rễ của mọi tội lỗi. - Donald Knuth

Trong hầu hết các trường hợp,> 90% thời gian của chương trình của bạn sẽ được sử dụng trong <10% mã và không có hồ sơ mã của bạn, bạn không biết đó là 10%. Vì vậy, đừng lo lắng về điều đó cho đến khi bạn nhận được phản hồi; nó có giá trị hơn nhiều để tối ưu hóa thời gian lập trình viên hơn thời gian chạy .


3

Việc lưu trữ một giá trị được tính toán trong một biến cục bộ sẽ hiệu quả hơn, giả sử trình biên dịch của bạn chưa tối ưu hóa điều đó cho bạn. Trong thực tế, đôi khi điều này được xây dựng thành một chức năng, trong một kỹ thuật gọi là ghi nhớ .

Tuy nhiên, hầu hết thời gian, hiệu quả đạt được là đủ nhỏ để được coi là không đáng kể. Khả năng đọc thường là mối quan tâm chính của bạn sau khi chính xác.

Điều đó đang được nói, sử dụng một biến cục bộ cũng thường xảy ra để làm cho nó dễ đọc hơn, mang lại cho bạn những điều tốt nhất của cả hai thế giới. Sử dụng ví dụ của bạn:

const char* text = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);

Điều này rõ ràng hơn nhiều đối với một người đọc con người. Bạn không chỉ không phải đọc lệnh gọi hàm dài đó trên mỗi dòng, rõ ràng rõ ràng rằng tất cả các widget đều nhận được cùng một văn bản.


2

Các biến về cơ bản là miễn phí, nhưng lệnh gọi hàm có chi phí không xác định, vì vậy luôn đi với biến.


1

Một nguyên tắc chung tôi tuân theo là nếu tôi không biết cách thực hiện một chức năng, nó thể tốn kém và nếu tôi biết rằng tôi chỉ cần giá trị từ chức năng đó một lần, tôi chỉ lưu trữ cục bộ. Ví dụ mã của bạn sau đó sẽ trở thành:

// define pointers to the items in my form
//...
int SelectedIndex = pLISTBOX->GetCurSel();
String text = pLISTBOX->GetItemData(SelectedIndex); //Ok, so maybe String isn't a valid type here but you get the idea...
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.