Có thực sự là một thực hành tốt vô hiệu hóa tối ưu hóa trong các giai đoạn phát triển và gỡ lỗi?


15

Tôi đã đọc Lập trình vi điều khiển PIC 16 bit trong C và có lời khẳng định này trong cuốn sách:

Tuy nhiên, trong quá trình phát triển và gỡ lỗi của một dự án, việc tắt tất cả các tối ưu hóa luôn là một cách tốt để chúng có thể sửa đổi cấu trúc của mã được phân tích và khiến cho việc đặt một bước và điểm dừng có vấn đề.

Tôi thú nhận tôi có một chút bối rối. Tôi không hiểu nếu tác giả nói rằng vì thời gian đánh giá C30 hoặc nếu đó thực sự là một thực hành tốt.

Tôi muốn biết nếu bạn thực sự sử dụng thực hành này, và tại sao?

Câu trả lời:


16

Đây là một tiêu chuẩn khá trong công nghệ phần mềm nói chung - khi bạn tối ưu hóa mã, trình biên dịch được phép sắp xếp lại mọi thứ khá nhiều theo ý muốn, miễn là bạn không thể thấy bất kỳ sự khác biệt nào trong hoạt động. Vì vậy, ví dụ, nếu bạn khởi tạo một biến trong mỗi lần lặp của vòng lặp và không bao giờ thay đổi biến trong vòng lặp, trình tối ưu hóa được phép di chuyển khởi tạo đó ra khỏi vòng lặp, để bạn không lãng phí thời gian với nó.

Nó cũng có thể nhận ra rằng bạn tính toán một số mà sau đó bạn không làm gì trước khi viết quá nhiều. Trong trường hợp đó, nó có thể loại bỏ tính toán vô dụng.

Vấn đề với tối ưu hóa là bạn sẽ muốn đặt một điểm dừng trên một số đoạn mã mà trình tối ưu hóa đã di chuyển hoặc loại bỏ. Trong trường hợp đó, trình gỡ lỗi không thể làm những gì bạn muốn (nói chung, nó sẽ đặt điểm dừng ở một nơi nào đó gần). Vì vậy, để làm cho mã được tạo gần giống với những gì bạn đã viết, bạn tắt tối ưu hóa trong quá trình gỡ lỗi - điều này đảm bảo rằng mã bạn muốn phá vỡ thực sự ở đó.

Bạn cần cẩn thận với điều này, tuy nhiên, vì tùy thuộc vào mã của bạn, tối ưu hóa có thể phá vỡ mọi thứ! Nói chung, mã bị hỏng bởi trình tối ưu hóa hoạt động chính xác thực sự chỉ là mã lỗi đang bị loại bỏ bởi một cái gì đó, vì vậy bạn thường muốn tìm hiểu tại sao trình tối ưu hóa phá vỡ nó.


5
Đối lập với lập luận này là trình tối ưu hóa có thể sẽ khiến mọi thứ nhỏ hơn và / hoặc nhanh hơn và nếu bạn có mã bị hạn chế về thời gian hoặc kích thước thì bạn có thể phá vỡ thứ gì đó bằng cách vô hiệu hóa tối ưu hóa và lãng phí thời gian để gỡ lỗi một vấn đề không xảy ra ' t thực sự tồn tại Tất nhiên, trình gỡ lỗi cũng có thể làm cho mã của bạn chậm hơn và lớn hơn.
Kevin Vermeer

Tôi có thể tìm hiểu thêm về điều này ở đâu?
Daniel Grillo

Tôi chưa làm việc với trình biên dịch C30 nhưng đối với trình biên dịch C18, có một ghi chú / hướng dẫn sử dụng cho trình biên dịch bao gồm những gì tối ưu hóa mà nó hỗ trợ.
Đánh dấu

@O Engenheiro: Kiểm tra tài liệu trình biên dịch của bạn để biết những tối ưu hóa mà nó hỗ trợ. Tối ưu hóa khác nhau tùy thuộc vào trình biên dịch, thư viện và kiến ​​trúc đích.
Michael Kohne

Một lần nữa, không phải cho trình biên dịch C30, nhưng gcc xuất bản một danh sách DÀI các tối ưu hóa khác nhau có thể được áp dụng. Bạn cũng có thể sử dụng danh sách này để có được tối ưu hóa chi tiết, trong trường hợp bạn có cấu trúc điều khiển cụ thể mà bạn muốn giữ nguyên. Danh sách ở đây: gcc.gnu.org/onlinesocs/gcc/Optizes-Options.html
Kevin Vermeer

7

Tôi đã gửi câu hỏi này cho Jack Ganssle và đây là những gì anh ấy đã trả lời tôi:

Daniel,

Tôi thích gỡ lỗi bằng cách sử dụng bất kỳ tối ưu hóa nào sẽ có trong mã được phát hành. NASA nói "kiểm tra những gì bạn bay, bay những gì bạn kiểm tra." Nói cách khác, đừng thực hiện kiểm tra và sau đó thay đổi mã!

Tuy nhiên, đôi khi người ta phải tắt tối ưu hóa để trình gỡ lỗi hoạt động. Tôi cố gắng tắt chúng chỉ trong các mô-đun tôi đang làm việc. Vì lý do đó, tôi tin vào việc giữ các tệp nhỏ, nói vài trăm dòng mã hoặc hơn thế.

Tốt nhất, Jack


Tôi nghĩ rằng có một sự khác biệt không có căn cứ giữa hai đoạn trong phản ứng này. Thử nghiệm đề cập đến một quy trình được cho là chứng minh rằng các sản phẩm mềm, cứng và / hoặc cứng hoạt động tốt. Gỡ lỗi là quá trình trong đó mã được chuyển qua hướng dẫn theo hướng dẫn để xem tại sao nó không hoạt động [chưa].
Kevin Vermeer

Thật tốt khi có một sự lựa chọn. Vì vậy, thử nghiệm có thể bao gồm nhiều giống / hoán vị có và không có tối ưu hóa. Bảo hiểm nhiều hơn, tốt hơn là thử nghiệm

@reemrevnivek, khi bạn gỡ lỗi, bạn cũng không thử nghiệm à?
Daniel Grillo

@O Engenheiro - Không. Tôi chỉ gỡ lỗi nếu thử nghiệm thất bại.
Kevin Vermeer

6

Phụ thuộc và điều này thường đúng với tất cả các công cụ không chỉ C30.

Tối ưu hóa thường loại bỏ và / hoặc cơ cấu lại mã theo nhiều cách khác nhau. Câu lệnh chuyển đổi của bạn có thể được thực hiện lại với cấu trúc if / other hoặc trong một số trường hợp có thể được xóa tất cả cùng nhau. y = x * 16 có thể được thay thế bằng một loạt các dịch chuyển trái, v.v. mặc dù loại tối ưu hóa cuối cùng này thường vẫn có thể được thực hiện, nhưng chủ yếu là cấu trúc lại câu lệnh điều khiển được ya.

Điều này có thể khiến trình gỡ lỗi không thể chuyển qua mã C của bạn vì các cấu trúc bạn xác định trong C không còn tồn tại, chúng được trình biên dịch thay thế hoặc sắp xếp lại thành thứ gì đó mà trình biên dịch tin rằng sẽ nhanh hơn hoặc sử dụng ít dung lượng hơn. Nó cũng có thể làm cho các điểm dừng không thể được thiết lập từ danh sách C vì hướng dẫn mà bạn ngắt có thể không còn tồn tại. Ví dụ: bạn có thể cố gắng đặt điểm dừng bên trong câu lệnh if, nhưng trình biên dịch có thể đã loại bỏ if đó. Bạn có thể cố gắng thiết lập một điểm dừng bên trong một vòng lặp hoặc cho vòng lặp nhưng trình biên dịch đã quyết định hủy bỏ vòng lặp đó để nó không còn tồn tại.

Vì lý do này nếu bạn có thể gỡ lỗi với tối ưu hóa, nó thường dễ dàng hơn. Bạn nên luôn luôn kiểm tra lại với tối ưu hóa trên. Đây là về cách duy nhất bạn sẽ phát hiện ra rằng bạn đã bỏ lỡ một điều quan trọngvolatile và nó gây ra những thất bại không liên tục (hoặc một số điều kỳ lạ khác).

Trong trường hợp phát triển nhúng, dù sao bạn cũng phải cẩn thận với việc tối ưu hóa. Cụ thể trong các phần của mã là thời gian quan trọng, ví dụ một số ngắt. Trong các trường hợp này, bạn nên mã hóa các bit quan trọng trong lắp ráp hoặc sử dụng các chỉ thị của trình biên dịch để đảm bảo các phần này không được tối ưu hóa để bạn biết chúng có thời gian thực hiện cố định hoặc thời gian chạy trường hợp xấu nhất cố định.

Các Gotcha khác có thể phù hợp mã vào uC, bạn có thể cần tối ưu hóa mật độ mã để đơn giản phù hợp với mã của bạn vào chip. Đây là một lý do tại sao thường là một ý tưởng tốt để bắt đầu với uC dung lượng ROM lớn nhất trong một gia đình và chỉ chọn một cái nhỏ hơn để sản xuất, sau khi mã của bạn bị khóa.


5

Nói chung, tôi sẽ gỡ lỗi với bất kỳ cài đặt nào tôi dự định phát hành. Nếu tôi sẽ phát hành mã được tối ưu hóa, tôi sẽ gỡ lỗi với mã được tối ưu hóa. Nếu tôi định phát hành mã chưa được tối ưu hóa, tôi sẽ gỡ lỗi với mã chưa được tối ưu hóa. Tôi làm điều này vì hai lý do. Đầu tiên, trình tối ưu hóa có thể tạo ra sự khác biệt đáng kể về thời gian để làm cho sản phẩm cuối hoạt động khác với mã không được tối ưu hóa. Thứ hai, mặc dù hầu hết đều khá tốt, các nhà cung cấp trình biên dịch đã mắc lỗi và mã được tối ưu hóa có thể tạo ra các kết quả khác nhau từ mã không được tối ưu hóa. Kết quả là, tôi muốn có được nhiều thời gian thử nghiệm nhất có thể với bất kỳ cài đặt nào tôi dự định phát hành.

Điều đó đang được nói, tối ưu hóa có thể làm cho việc gỡ lỗi khó khăn như đã lưu ý trong các câu trả lời trước. Nếu tôi tìm thấy một phần mã cụ thể khó gỡ lỗi, tôi sẽ tạm thời tắt trình tối ưu hóa, thực hiện gỡ lỗi để mã hoạt động, sau đó bật lại trình tối ưu hóa và kiểm tra lại.


1
Nhưng bước đơn mã có thể gần như không thể với tối ưu hóa được bật. Gỡ lỗi với tối ưu hóa tắt và chạy thử nghiệm đơn vị của bạn với mã phát hành.
Rocketmagnet

3

Chiến lược bình thường của tôi là phát triển với tối ưu hóa cuối cùng (tối đa cho kích thước hoặc tốc độ phù hợp), nhưng tạm thời tắt tối ưu hóa nếu tôi cần gỡ lỗi theo dõi một cái gì đó. Điều này làm giảm nguy cơ lỗi xuất hiện do thay đổi mức tối ưu hóa.

Một chế độ thất bại điển hình là khi tăng tối ưu hóa khiến các lỗi chưa từng thấy trước đó xuất hiện do bạn không khai báo các biến là biến động khi cần thiết - điều này rất cần thiết để báo cho trình biên dịch biết những điều không nên được tối ưu hóa '.


2

Sử dụng bất kỳ hình thức nào bạn sẽ phát hành cùng, trình gỡ lỗi và biên dịch để gỡ lỗi ẩn rất nhiều (rất nhiều) lỗi mà bạn không thấy cho đến khi bạn biên dịch để phát hành. Đến lúc đó, việc tìm những lỗi đó khó hơn nhiều so với gỡ lỗi khi bạn đi. 20 năm nay và tôi chưa bao giờ sử dụng gdb hay trình gỡ lỗi nào khác, không cần phải xem biến hoặc bước đơn. Hàng trăm đến hàng ngàn dòng mỗi ngày. Vì vậy, nó là có thể, không được dẫn đến suy nghĩ khác.

Biên dịch để gỡ lỗi sau đó biên dịch để phát hành có thể và sẽ mất gấp đôi đến hơn hai lần nỗ lực. Nếu bạn gặp phải một ràng buộc và phải sử dụng một công cụ như trình gỡ lỗi thì hãy biên dịch để trình gỡ lỗi xử lý vấn đề cụ thể, sau đó trở lại hoạt động bình thường.

Các vấn đề khác cũng đúng như trình tối ưu hóa làm cho mã nhanh hơn để được nhúng cụ thể thay đổi thời gian của bạn với các tùy chọn trình biên dịch và điều đó có thể ảnh hưởng đến chức năng của chương trình của bạn, ở đây một lần nữa sử dụng lựa chọn biên dịch có thể phân phối trong toàn bộ giai đoạn. Trình biên dịch cũng là chương trình và có lỗi và tối ưu hóa mắc lỗi và một số không có niềm tin vào điều đó. Nếu đó là trường hợp không có gì sai với nó biên dịch mà không tối ưu hóa, chỉ cần làm theo cách đó mọi lúc. Đường dẫn tôi thích là biên dịch để tối ưu hóa sau đó nếu tôi nghi ngờ có vấn đề về trình biên dịch sẽ vô hiệu hóa tối ưu hóa nếu việc đó khắc phục nó thường quay đi quay lại đôi khi kiểm tra đầu ra của trình biên dịch để tìm hiểu tại sao.


1
+1 chỉ để giải thích câu trả lời hay của bạn: thường biên dịch trong chế độ "gỡ lỗi" sẽ đệm ngăn xếp / đống xung quanh các biến với không gian không được phân bổ để giảm thiểu lỗi viết tắt và định dạng chuỗi nhỏ. Bạn sẽ thường xuyên gặp sự cố thời gian chạy tốt hơn nếu bạn biên dịch trong bản phát hành.
Morten Jensen

2

Tôi luôn phát triển mã với -O0 (tùy chọn gcc để tắt tối ưu hóa). Khi tôi cảm thấy mình đang ở điểm mà tôi muốn bắt đầu để mọi thứ hướng đến một bản phát hành nhiều hơn, tôi sẽ bắt đầu với -Os (tối ưu hóa kích thước) vì nói chung, càng nhiều mã bạn có thể giữ trong bộ đệm thì càng tốt, ngay cả khi nó không được siêu tối ưu hóa.

Tôi thấy rằng gdb hoạt động tốt hơn nhiều với mã -O0 và việc theo dõi sẽ dễ dàng hơn rất nhiều nếu bạn phải bước vào lắp ráp. Chuyển đổi giữa -O0 và -Os cũng cho phép bạn xem những gì trình biên dịch đang làm với mã của bạn. Đôi khi, đây là một cách giáo dục khá thú vị và cũng có thể phát hiện ra các lỗi của trình biên dịch ... những điều khó chịu đó khiến bạn phải nhổ tóc ra để tìm ra điều gì sai với mã của bạn!

Nếu tôi thực sự cần, tôi sẽ bắt đầu thêm vào các phần -fdata và -fcode với phần --gc, cho phép trình liên kết loại bỏ toàn bộ các chức năng và phân đoạn dữ liệu không thực sự được sử dụng. Có rất nhiều điều nhỏ bạn có thể sửa đổi để cố gắng thu nhỏ mọi thứ hơn nữa hoặc làm cho mọi thứ nhanh hơn, nhưng nói chung đây là những thủ thuật duy nhất tôi sử dụng, và bất cứ điều gì phải nhỏ hơn hoặc nhanh hơn tôi sẽ xử lý -tập hợp.


2

Có, vô hiệu hóa tối ưu hóa trong quá trình gỡ lỗi đã được thực hiện tốt nhất trong một thời gian, vì ba lý do:

  • (a) nếu bạn định thực hiện một bước chương trình với trình gỡ lỗi cấp cao, điều đó hơi khó hiểu.
  • (a) (lỗi thời) nếu bạn định gỡ lỗi một bước chương trình với trình gỡ lỗi ngôn ngữ lắp ráp, điều đó sẽ ít gây nhầm lẫn hơn. (Nhưng tại sao bạn lại bận tâm với điều này khi bạn có thể sử dụng trình gỡ lỗi cấp cao?)
  • (b) (lỗi thời) có lẽ bạn sẽ chỉ chạy chương trình thực thi cụ thể này một lần, sau đó thực hiện một số thay đổi và biên dịch lại. Thật lãng phí thời gian của một người để chờ thêm 10 phút trong khi trình biên dịch "tối ưu hóa" khả năng thực thi cụ thể này, khi đó sẽ tiết kiệm ít hơn 10 phút thời gian chạy. (Điều này không còn phù hợp với các PC hiện đại có thể biên dịch một vi điều khiển thông thường có thể thực thi được, với tối ưu hóa hoàn toàn, trong vòng chưa đầy 2 giây).

Nhiều người thậm chí còn đi xa hơn theo hướng này, và tàu với các xác nhận được bật .


Một bước thông qua mã lắp ráp có thể rất hữu ích để chẩn đoán các trường hợp trong đó mã nguồn thực sự chỉ định một cái gì đó khác với giao diện của nó (ví dụ: "longvar1 & = ~ 0x40000000; longvar2 & = ~ 0x80000000;") hoặc khi trình biên dịch tạo mã lỗi . Tôi đã theo dõi một số vấn đề khi sử dụng trình gỡ lỗi mã máy mà tôi thực sự không nghĩ rằng mình có thể theo dõi bất kỳ cách nào khác.
supercat

2

Đơn giản: tối ưu hóa tốn nhiều thời gian và có thể vô dụng nếu bạn phải thay đổi đoạn mã đó sau này trong quá trình phát triển. Vì vậy, họ cũng có thể là một sự lãng phí thời gian và tiền bạc.
Chúng rất hữu ích cho các mô-đun đã hoàn thành, tuy nhiên; các phần của mã có lẽ sẽ không cần thay đổi nữa.


2

nó chắc chắn có ý nghĩa trong trường hợp điểm dừng ... vì trình biên dịch có thể loại bỏ rất nhiều câu lệnh không thực sự ảnh hưởng đến bộ nhớ.

xem xét một cái gì đó như:

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

có thể được tối ưu hóa hoàn toàn (bởi vì i chưa bao giờ đọc). nó sẽ nhìn từ quan điểm của điểm dừng của bạn rằng nó đã bỏ qua tất cả các mã đó, khi về cơ bản nó thậm chí không có ở đó .... Tôi cho rằng đó là lý do tại sao trong các loại chức năng ngủ, bạn sẽ thường thấy một cái gì đó như:

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;

1

Nếu bạn đang sử dụng trình gỡ lỗi thì tôi sẽ vô hiệu hóa tối ưu hóa và bật gỡ lỗi.

Cá nhân tôi thấy rằng trình gỡ lỗi PIC gây ra nhiều vấn đề hơn nó giúp tôi khắc phục.
Tôi chỉ sử dụng printf () sang USART để gỡ lỗi các chương trình của tôi được viết bằng C18.


1

Hầu hết các đối số chống lại việc tối ưu hóa trong quá trình biên dịch của bạn đều được đưa ra:

  1. sự cố với gỡ lỗi (kết nối JTAG, điểm dừng, v.v.)
  2. sai thời gian phần mềm
  3. sh * t ngừng hoạt động chính xác

IMHO hai cái đầu tiên là hợp pháp, thứ ba không quá nhiều. Điều này thường có nghĩa là bạn có một số mã xấu hoặc đang dựa vào việc khai thác ngôn ngữ / triển khai không an toàn hoặc có thể tác giả chỉ là một người hâm mộ của hành vi không xác định của Bác.

Blog Embedded in Academia có một hoặc hai điều để nói về hành vi không xác định và bài đăng này là về cách trình biên dịch khai thác nó: http://blog.regehr.org/archives/761


Một lý do khác có thể là trình biên dịch có thể bị chậm một cách khó chịu khi bật tối ưu hóa.
supercat
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.