Lưu trữ sử dụng các phương thức so với mã sao chép


7

Khi bạn có các đoạn mã thực sự nhỏ được lặp đi lặp lại nhiều lần trong chương trình điều khiển vi mô của bạn, tốt hơn là viết một phương thức? Tôi có nghĩa là tốt hơn về hiệu suất và sử dụng lưu trữ. Tôi biết rằng khi lập trình cho PC, việc lặp lại mã là một điều không nên, nhưng còn vi điều khiển thì sao?

Trong ví dụ sau (Arduino Nano với màn hình cảm ứng nắp 2,8 "Adaf nhung), đoạn mã sẽ xem nếu nút quay lại được nhấn trong một số màn hình. Tôi đã kiểm tra trong Arduino IDE mỗi bộ lưu trữ có bao nhiêu dung lượng:

Phiên bản 1, 15114 byte cho chương trình hoàn chỉnh:

void battScreen(TS_Point p){
  // some other code
  // ....
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void alarmScreen(TS_Point p){
  // some other code
  //...
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void pumpScreen(TS_Point p){
  // some other code
  // ...
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

Phiên bản 2, 15234 Byte cho chương trình hoàn chỉnh:

void checkBack(TS_Point p){
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void battScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

void alarmScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

void pumpScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

Như bạn có thể thấy, phiên bản 2 với phương thức bổ sung sẽ chiếm thêm 120 Byte. Có một quy tắc khi tạo ra một phương thức mới thay vì sao chép mã?


Có thể câu hỏi sau đây cũng cung cấp cho bạn một số thông tin chi tiết: stackoverflow.com/questions/144993/ trên
Michel Keijzers

Sự so sánh của bạn khá lạ lùng; các chức năng chiếm không gian LESS hơn là sao chép mã. Tôi sẽ thử triển khai nó, vì tôi nghĩ có một số vấn đề khác với mã cụ thể của bạn
frarugi87

2
Ngoài ra (ngoại trừ kết quả thú vị), không tối ưu hóa nếu không cần thiết. Chủ yếu là khả năng đọc, khả năng bảo trì ủng hộ hiệu suất trên hoặc bộ nhớ flash được sử dụng.
Michel Keijzers

1
@ frarugi87 Điều này là có thể, mặc dù tôi chỉ thay đổi mã như trong câu hỏi. Có lẽ (một phần) lý do là những gì Code Gorilla đã viết trong câu trả lời của mình với các biến sao chép.
Lehue

Câu trả lời:


5

Nó .... phụ thuộc

Một cuộc gọi đến chức năng liên quan đến một số điều

  1. Lưu bối cảnh
  2. Di chuyển (các) tham số đến các thanh ghi thích hợp
  3. Nhập chức năng
  4. Hành hình
  5. Cuối cùng lưu giá trị trả về vào một thanh ghi
  6. Khôi phục bối cảnh
  7. Cuối cùng sao chép giá trị trả về vào biến

Như bạn có thể thấy, khá nhiều thứ. Các điểm in đậm cũng là những điểm được thực thi nếu bạn không sử dụng hàm.

Xin lưu ý rằng nếu các hàm này là "phương thức", thì bạn cũng có một tham số "ẩn" (đối tượng bạn đang áp dụng phương thức này)

Vậy tại sao phải sử dụng chức năng? Chà, bạn sẽ phải viết mã này trong flash chỉ một lần thay vì nhiều lần hơn.

Điều gì là tốt nhất? Nó phụ thuộc vào ứng dụng của bạn.

  • Bạn đang gọi chức năng này chỉ một lần? Không sử dụng chức năng
  • Chức năng của bạn thực sự ngắn (ví dụ, một lớp lót) *? Không sử dụng chức năng
  • Bạn đang hết bộ nhớ flash? Sử dụng một chức năng
  • Bạn cần rất nhiều tốc độ? Không sử dụng chức năng (bạn thêm chi phí)
  • Bạn không thuộc một trong những trường hợp trước? Làm như bạn thích

    • Với một lớp lót, tôi có nghĩa là một cái gì đó như, ví dụ , byte sum(byte a, byte b) { return a + b; }; trong trường hợp này, hàm cũng chiếm nhiều không gian hơn, vì lệnh của hàm gọi tương thích với tổng byte

Xin lưu ý rằng thông thường khả năng đọc nhiều hơn quan trọng hơn hiệu suất, vì vậy có lẽ nên sử dụng các chức năng. Ý tôi là, trong trường hợp của bạn, tôi có thể sử dụng chúng trừ khi có vấn đề

Phải làm gì nếu bạn không muốn sử dụng một chức năng? Vâng, có những kỹ thuật khác:

  • Đánh dấu chức năng là nội tuyến; đây là một gợi ý cho trình biên dịch không phải để tạo một hàm mà là sao chép này mỗi lần. Đây chỉ là một gợi ý, vì vậy trình biên dịch có thể bỏ qua điều này
  • Thay vì một hàm viết một macro. Điều này sẽ được thay thế mỗi lần, nhưng mã sẽ được duy trì. Như một nhược điểm, việc gỡ lỗi với các phương thức thông thường khó hơn nhiều.

Chỉ cần một nhận xét: thông thường các trình biên dịch thông minh có thể tự đoán đâu là tùy chọn tốt nhất (nội tuyến hoặc hàm) cho các hàm không nội tuyến. Vì vậy, bạn cũng có thể tin tưởng trình biên dịch trong 90% các trường hợp


1
Bạn chưa đề cập đến tối ưu hóa: nếu bạn chỉ sử dụng một hàm thì trình biên dịch rất có thể sẽ chỉ đặt mã nội tuyến, do đó tránh được chi phí hoạt động của hàm. Bạn cũng có thể buộc một hàm là nội tuyến, vì vậy bạn có sự tiện lợi không có mã trùng lặp cùng với hiệu quả của mã nội tuyến.
Majenko

Việc tối ưu hóa là trong nhận xét cuối cùng; Tôi có thể đã làm nổi bật nó nhiều hơn. Hơn nữa, làm thế nào bạn có thể buộc trình biên dịch để nội tuyến? Tôi nghĩ rằng từ khóa "nội tuyến" chỉ là một gợi ý; Có một từ khóa khác để buộc nội tuyến?
frarugi87

1
__attribute__((always_inline)).
Majenko

2

Lý do tôi có xu hướng hướng tới phương pháp thứ hai nhiều hơn là có ít cơ hội hơn cho các lỗi trong phiên bản thứ hai.

Nếu kích thước lưu trữ là mối quan tâm chính và bạn biết rằng việc lặp lại mã yêu cầu lưu trữ ít hơn, thì tôi sẽ xác định nó là một macro và đạt được kết quả tốt nhất của cả hai thế giới.

Bạn có thể cải thiện mã mặc dù. Tại thời điểm bạn chuyển các bản sao của biến vào các hàm, việc chuyển các tham chiếu const có thể sử dụng ít chỗ hơn,void checkBack(const TS_Point& p){


sử dụng void checkBack(const TS_Point& p)thực sự đã giảm kích thước 80 byte. Cảm ơn vì điều đó!
Lehue

Những gì có vẻ mặc dù kỳ lạ, đó là const type& variable chỉ giảm kích thước với TS_Pointcác biến, không phải với int, uint16_thoặc các loại bình thường khác, nơi mà nó làm tăng kích thước. Tôi biết đây không phải là câu hỏi, nhưng bạn có biết lý do cho điều đó không?
Lehue
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.