Tại sao tôi nên quan tâm đến hiệu suất và hiệu quả vi mô?


71

Nhiều câu hỏi và câu trả lời trên các trang C / C ++, cụ thể hoặc gián tiếp thảo luận về các vấn đề hiệu suất vi mô (chẳng hạn như chi phí hoạt động gián tiếp so với chức năng trực tiếp so với nội tuyến) hoặc sử dụng thuật toán O (N 2 ) so với O (N log N) trên một danh sách 100 mặt hàng.

Tôi luôn viết mã mà không quan tâm đến hiệu suất vi mô và ít quan tâm đến hiệu suất vĩ mô, tập trung vào mã dễ bảo trì, mã đáng tin cậy, trừ khi hoặc cho đến khi tôi biết tôi có vấn đề.

Câu hỏi của tôi là tại sao một số lượng lớn các lập trình viên quan tâm rất nhiều? Có thực sự là một vấn đề đối với hầu hết các nhà phát triển, tôi đã đủ may mắn để không phải lo lắng quá nhiều về nó, hay tôi là một lập trình viên tồi?


5
+1, câu hỏi chung tốt.
iammilind

+1 câu hỏi hay..Tôi đã thêm 2 thẻ .. hy vọng bạn không bận tâm về điều đó.

2
Tôi đứng đầu hai câu trích dẫn tuyệt vời 1) "Tối ưu hóa sớm là gốc rễ của mọi tội lỗi." 2) 80% thời gian của bạn sẽ được sử dụng 20% ​​mã của bạn (quy tắc 80/20).
James Khoury

2
Tôi nhận thấy một vài câu trả lời nói về ví dụ O (n * n) của tôi. Tôi đã chỉ định rõ ràng một danh sách 100 mục, nhưng họ vẫn khăng khăng rằng O (nlogn) tốt hơn, nêu rõ các cải tiến hiệu suất nếu danh sách, trong phần kết nối, đi đến 1000 hoặc hàng triệu. Đây có phải là nỗi ám ảnh tối ưu hóa vi mô bởi vì các lập trình viên đang lập trình cho các yêu cầu có thể trong tương lai chứ không phải là yêu cầu hiện thực? (Tôi đã nghe điều đó ở đâu trước đây ...)
mattnz

5
@James trích dẫn đầy đủ từ Donald Knuth là "Chúng ta nên quên đi những hiệu quả nhỏ, nói khoảng 97% thời gian: tối ưu hóa sớm là gốc rễ của mọi tội lỗi". Sẽ có một số câu trả lời tốt về 3% còn lại trong chủ đề này.
StuperUser

Câu trả lời:


14

Trong thực tế, hiệu suất hiếm khi là một vấn đề cần được quản lý ở mức độ chi tiết đó. Thật đáng để theo dõi tình hình nếu bạn biết bạn sẽ lưu trữ và thao tác với lượng dữ liệu khổng lồ, nhưng nếu không, bạn đã đúng, và tốt hơn hết là giữ mọi thứ đơn giản.

Một trong những cái bẫy dễ rơi vào - đặc biệt là trong C và C ++, nơi bạn có quyền kiểm soát chi tiết như vậy - là tối ưu hóa quá sớm và ở mức quá tốt. Nói chung, quy tắc là: A) không tối ưu hóa cho đến khi bạn phát hiện ra mình có vấn đề và B) không tối ưu hóa bất cứ điều gì bạn chưa chứng minh là khu vực có vấn đề bằng cách sử dụng trình lược tả.

Một hệ quả của B) là: các lập trình viên nổi tiếng là không giỏi trong việc dự đoán các điểm nghẽn về hiệu suất của họ, mặc dù, đối với một người, họ nghĩ rằng họ giỏi về điều đó. Sử dụng một trình lược tả và tối ưu hóa các phần chậm hoặc thay đổi thuật toán nếu một phần của mã được gọi quá nhiều lần, do đó nó gây ra sự cố.


6
Một cái khác: mã khởi tạo thực thi một lần thường không cần tối ưu hóa, vì vậy hãy tìm nơi khác.
Mike DeSimone

3
Phụ thuộc vào mức độ thường xuyên "một lần". Khi chạy ./configure, tôi muốn nói rằng có thể dành tới 75% thời gian chạy cho mã "khởi tạo" trong các chương trình mà tập lệnh chạy. 25-50% thậm chí có thể được chi cho liên kết động.
R ..

12
Quy tắc A là một quy tắc khủng khiếp. Kiến trúc của một hệ thống đóng một vai trò trong hiệu suất và nếu sau này bạn phát hiện ra kiến ​​trúc của bạn không thể hỗ trợ các yêu cầu về hiệu suất của bạn thì về cơ bản bạn đã bị lừa. Vì vậy, trong khi bạn có thể vượt qua các chi tiết tốt, hoàn toàn bỏ qua điều này ngay từ đầu chỉ là sai.
edA-qa mort-ora-y

3
@ edA-qa: Tôi đã từng nghĩ như vậy, nhưng trong nhiều năm qua đã có nhiều dự án bị đình trệ hoặc thất bại trước khi bất kỳ cân nhắc về hiệu suất nào trở thành mối quan tâm. Mỗi lần tôi gặp vấn đề về hiệu năng, bản sửa lỗi có chi phí tương đối thấp, vài ngày hoặc vài tuần, Không có gì đáng lo ngại hơn bất kỳ "lỗi" nào khác phát hiện ra một bản sửa lỗi trong quá trình phát triển. Tuy nhiên, jsut giống như bất kỳ mục rủi ro otehr nào, các yếu tố hiệu suất cần được xác định và giảm thiểu sớm trong dự án.
mattnz

5
OP đã hỏi tại sao rất nhiều người quan tâm và tôi không biết câu trả lời này thực sự trả lời câu hỏi như thế nào, trừ khi OP quan tâm hơn khi nghe ai đó nói "đừng lo lắng về điều đó!".
bụi đỏ

54

Tôi nghĩ rằng tất cả mọi thứ trong danh sách của bạn là tối ưu hóa vi mô, thường không nên xem xét, ngoại trừ

sử dụng thuật toán O (n * n) so với O (NlogN) trên danh sách 100 mục

mà tôi nghĩ nên xem xét Chắc chắn, danh sách đó là 100 mặt hàng ngay bây giờ và mọi thứ đều nhanh chóng đối với n nhỏ , nhưng tôi sẵn sàng đặt cược sớm rằng cùng một mã sẽ được sử dụng lại cho danh sách vài triệu dòng và mã vẫn sẽ có làm việc hợp lý

Chọn đúng thuật toán không bao giờ là tối ưu hóa vi mô. Bạn không bao giờ biết loại dữ liệu nào mà cùng một mã sẽ được sử dụng trong hai tháng hoặc hai năm sau đó. Không giống như "tối ưu hóa vi mô" dễ áp ​​dụng với hướng dẫn của trình lược tả, các thay đổi thuật toán thường yêu cầu thiết kế lại đáng kể để sử dụng hiệu quả các thuật toán mới. (Ví dụ: một số thuật toán yêu cầu dữ liệu đầu vào đã được sắp xếp, điều này có thể buộc bạn phải sửa đổi các phần quan trọng của ứng dụng để đảm bảo dữ liệu được sắp xếp)


36
+1 cho "Chọn thuật toán phù hợp không bao giờ là tối ưu hóa vi mô."

9
Tôi cũng vậy, nhưng lưu ý rằng việc chọn thuật toán tối ưu hóa lớn khi kích thước dữ liệu của bạn chắc chắn nhỏ có thể gây bất lợi cho thời gian phát triển, kích thước chương trình và thậm chí là sử dụng bộ nhớ. Nếu bạn đang sắp xếp các ván bài xì phé, bạn có thực sự muốn viết quicksort, smoothsort hoặc mergesort không? Tôi sẽ bắt đầu với sắp xếp chèn đơn giản hoặc sử dụng một mạng sắp xếp.
R ..

8
Điều đó thật buồn cười. Trong một chủ đề về tối ưu hóa vi mô, rất nhiều nhà bình luận vi mô tối ưu hóa các câu trả lời. ;)
Bảo mật

5
"Tôi sẵn sàng đặt cược sớm rằng cùng một mã sẽ được sử dụng lại cho một danh sách vài triệu dòng": Điều đó hoàn toàn phụ thuộc vào miền vấn đề. Ví dụ: nếu bạn đang viết một thuật toán cờ vua, bạn có thể chắc chắn chắc chắn rằng kích thước bàn cờ sẽ không thay đổi. Nếu bạn lập trình một chiếc xe độc lập, số lượng bánh sẽ không phát triển nhanh, một trong hai.
nikie

3
Tôi không thích "chọn thuật toán phù hợp không bao giờ là tối ưu hóa vi mô" bởi vì nó HOÀN TOÀN đúng, với bản chất của từ "đúng". Tuy nhiên, tôi cảm thấy hàm ý của bạn thực sự là thuật toán "nhanh nhất hoặc hiệu quả nhất", điều mà tôi không đồng ý. Chọn thuật toán hiệu quả nhất là lựa chọn sai nếu mất nhiều thời gian để thực hiện và tốc độ hoặc không gian của phân khúc đó hầu như không thành vấn đề.
Casey Patton

18

Cách đây một thời gian, trong công việc đầu tiên của tôi, tôi đã viết mã cho các hệ thống nhúng. Các hệ thống này sử dụng bộ vi xử lý 8086 và có bộ nhớ hạn chế. Chúng tôi đã sử dụng trình biên dịch Intel C. Một hệ thống tôi đã xây dựng cần thiết để truy cập vào một cấu trúc 3-d. Tôi đã xây dựng nó giống như cuốn sách đã nói với tôi: gọi malloc cho 3 chiều, sau đó phân bổ các hàng cho kích thước tiếp theo, sau đó gọi calloc cho các nút cuối.

Nó khá phức tạp (đối với tôi vào thời điểm đó), tôi đã phải thực hiện điều chỉnh đường cong, kiểm soát quá trình ANOVA và phân tích bình phương. Không có thư viện đã làm điều này cho chúng tôi; chúng tôi đã phải viết tất cả và phù hợp với tất cả lên 8086.

Hệ thống chạy như một con chó. Sau khi lập hồ sơ nhanh, tôi phát hiện ra rằng một trong những vấn đề lớn nhất là việc cấp phát. Để khắc phục sự cố, tôi đã từ bỏ tất cả các cuộc gọi đến malloc và tự quản lý bộ nhớ của mình cho một khối lớn bộ nhớ.


Trong một trường hợp khác trong cùng một công việc, khách hàng đã phàn nàn về thời gian phản hồi trên hệ thống kiểm soát quy trình thống kê của họ. Nhóm trước tôi đã thiết kế hệ thống "phần mềm PLC" nơi các nhà khai thác có thể sử dụng logic boolean để kết hợp các tín hiệu và công tắc ngắt. Họ đã viết nó bằng một ngôn ngữ đơn giản hóa, ngày nay chúng ta gọi là "ngôn ngữ cụ thể miền". khi tôi nhớ nó trông giống như ((A1 + B1) > 4) AND (C1 > C2)và như vậy.

Thiết kế ban đầu đã phân tích cú pháp và giải thích chuỗi đó mỗi lần nó được đánh giá. Trên bộ xử lý dịch sởi của chúng tôi, việc này tiêu tốn rất nhiều thời gian và điều đó có nghĩa là bộ điều khiển quy trình không thể cập nhật nhanh như quy trình đang chạy.

Tôi đã có một cái nhìn mới về nó và quyết định rằng tôi có thể dịch logic đó thành mã lắp ráp, trong thời gian chạy. Tôi đã phân tích cú pháp một lần và sau đó mỗi lần nó chạy, ứng dụng được gọi là một hàm được tạo động. Kiểu như một số virus làm ngày hôm nay, tôi đoán vậy (nhưng tôi không thực sự biết). Kết quả là hiệu suất tăng gấp 100 lần, khiến khách hàng và sếp của tôi thực sự rất hạnh phúc.

Mã mới gần như không thể duy trì được, vì tôi đã xây dựng một trình biên dịch tùy chỉnh . Nhưng lợi thế về hiệu suất vượt xa nhược điểm bảo trì.


Gần đây tôi đang làm việc trên một hệ thống cần phân tích cú pháp XML, một cách linh hoạt. Các tập tin lớn hơn sẽ mất nhiều thời gian hơn. Điều này rất nhạy cảm về hiệu suất; quá chậm của một phân tích cú pháp sẽ khiến UI trở nên hoàn toàn không thể sử dụng được.

Những loại điều này xuất hiện tất cả các thời gian.


Vì vậy, .... đôi khi bạn muốn mã dễ bảo trì, dễ viết. Đôi khi bạn muốn mã chạy nhanh. Sự đánh đổi là quyết định kỹ thuật bạn cần thực hiện, trên mỗi dự án.


9
Trong tất cả các ví dụ của bạn, chi phí tối ưu hóa nó sau đó không cao hơn nhiều so với việc viết mã nhanh ngay từ đầu. Vì vậy, viết mã chậm hơn đơn giản trước và sau đó tối ưu hóa khi cần thiết hoạt động tốt trong tất cả chúng.
CodeInChaos

6
@CodeInChaos: Câu trả lời không yêu cầu khác. Nó nói lên câu hỏi của OP "Tại sao tôi nên quan tâm đến hiệu suất và hiệu quả vi mô?" Các vấn đề tiền tối ưu hóa chỉ được suy luận bởi những người trả lời khác.
webbiedave

12

Nếu bạn đang xử lý hình ảnh lớn và lặp lại qua từng pixel, thì việc điều chỉnh hiệu suất có thể rất quan trọng.


2
+1 - cũng vậy, tài chính tần số cao, bất kỳ loại bộ mã hóa / giải mã âm thanh / video, mô phỏng và mô hình hóa nào (ví dụ như trò chơi), các bit trên toàn hệ thống như bộ lập lịch CPU và trình quản lý bộ nhớ, v.v.
Billy ONeal

3
THỂ rất quan trọng, nhưng chỉ quan trọng sau khi bạn đã chứng minh điều đó để được như vậy bạn đã cấu hình nó là nơi bạn nghĩ rằng vấn đề là. (Gợi ý: có lẽ nó không ở đó.)
CHỈ CẦN HOẠT ĐỘNG CỦA TÔI

2
@ CHỈ CẦN HOẠT ĐỘNG CỦA TÔI: thực ra, để xử lý ảnh, xử lý dữ liệu thường là người tiêu dùng lớn thứ hai (I / O vẫn là lớn nhất). Tuy nhiên, tối ưu hóa cho I / O đòi hỏi rất nhiều thiết kế khác thường / điên rồ và sự chấp nhận của họ bởi các lập trình viên đồng nghiệp, và đôi khi không thể cải thiện hoàn toàn. Tuy nhiên, phần xử lý thường song song một cách rõ ràng, do đó chúng dễ dàng gặt hái được lợi ích. (Điều chỉnh của một người có thể được người khác xem là triển khai sách giáo khoa trực tiếp ... trừ khi bạn đạt đến cấp độ VirtualDub)
rwong

12

Hãy để tôi nói cho bạn biết một chút về lý do đằng sau văn hóa.

Nếu bạn gần 40 hơn 20 tuổi và bạn đã lập trình để kiếm sống qua những năm trưởng thành, thì bạn đã đến tuổi khi C ++ thực sự là trò chơi duy nhất trong thị trấn, các ứng dụng máy tính để bàn là chuẩn mực và phần cứng vẫn còn phần mềm bị tụt hậu rất nhiều về khả năng băng thông / hiệu năng.

  • Chúng tôi thường phải thực hiện các thủ thuật lập trình ngu ngốc để có thể đọc các tệp lớn (> 2G) ...
  • Chúng tôi đã từng lo lắng về kích thước thực thi ...
  • Chúng tôi đã từng lo lắng về việc các chương trình của chúng tôi đã tiêu thụ bao nhiêu bộ nhớ ...
  • Chúng tôi thường xuyên đưa ra thời gian thuật toán so với các quyết định đánh đổi không gian ...
  • Ngay cả ở mặt sau, chúng tôi đã phải viết các chương trình CGI bằng C hoặc C ++ cho bất cứ điều gì để xử lý không. của RPS ... Đó là một số đơn đặt hàng có cường độ nhanh hơn.
  • Chúng tôi đã từng chạy thử nghiệm về giá trị hiệu suất giữa delphi / c ++ / vb!

Rất ít người phải lo lắng về những điều này ngày hôm nay.

Tuy nhiên, 10 năm trước bạn vẫn phải lo lắng về việc phần mềm của mình bị tải xuống qua modem 56kb và được chạy trên PC 5 tuổi ... Bạn có nhớ PC đã hoạt động thế nào vào năm 1996 không? Hãy nghĩ về 4GB ổ cứng, bộ xử lý 200Mhz và RAM 128Mb ...

Còn máy chủ của 10 năm trước? Máy chủ "thế hệ tiếp theo" của Dell có giá 2000 USD và đi kèm với bộ xử lý pentium 2Ghz ( 2 ), 2Gb hoặc Ram và ổ cứng 20Gb.

Nó chỉ đơn giản là một trò chơi bóng khác nhau , và tất cả những kỹ sư "cao cấp" có 10 năm kinh nghiệm (những người có khả năng trả lời câu hỏi của bạn), đã cắt răng trong môi trường đó.


1
20 năm kinh nghiệm bổ sung cũng có nghĩa là chúng ta đã bị đốt cháy từ quá trình tối ưu hóa nhiều lần, nhiều lần và tránh làm những việc có thể cần sau này. Cùng một lý do tôi không đập ngón tay cái của mình (nhiều) trong khi sử dụng búa.
Blrfl

1
mở vòng lặp <shudder>
red-Dirt

5
và ngày nay tất cả những đứa trẻ nghĩ rằng băng thông, CPU và bộ nhớ là không giới hạn đang tìm thấy các ứng dụng di động của chúng không hoạt động tốt.
gbjbaanb

9

đã có 10 câu trả lời ở đây và một số câu trả lời rất hay, nhưng vì đây là một thú cưng cá nhân của tôi ...

tối ưu hóa sớm mà a) mất nhiều thời gian hơn so với giải pháp đơn giản b) giới thiệu nhiều mã hơn trong đó giải pháp đơn giản sẽ chỉ bằng một nửa kích thước và một nửa độ phức tạp và c) làm cho mọi thứ trở nên dễ đọc hơn là TUYỆT ĐỐI nên tránh. Tuy nhiên, nếu nhà phát triển có lựa chọn giữa việc sử dụng vector std :: map hoặc std :: và anh ta chọn bộ sưu tập sai vì thiếu hiểu biết thuần túy cho hiệu suất cũng tệ nếu không nói là tệ hơn so với tối ưu hóa sớm. Điều gì sẽ xảy ra nếu bạn có thể thay đổi một chút mã của mình ngày hôm nay, duy trì khả năng đọc, giữ độ phức tạp như nhau, nhưng làm cho nó hiệu quả hơn, bạn sẽ làm điều đó? Hay bạn sẽ gọi nó là "tối ưu hóa sớm"? Tôi thấy rằng nhiều người thậm chí sẽ không nghĩ rằng bằng cách này hay cách khác.

Khi tôi là người khuyên "tối ưu hóa vi mô" đòi hỏi rất ít thay đổi và tôi đã nhận được câu trả lời giống như bạn vừa nói, "bạn không nên tối ưu hóa quá sớm. Hãy làm cho nó hoạt động và chúng ta sẽ thay đổi nó sau này nếu có vấn đề về hiệu suất ". Phải mất một vài bản phát hành trước khi chúng tôi sửa nó. Và vâng, đó là một vấn đề hiệu suất.

Mặc dù tối ưu hóa sớm có thể không tốt, tôi nghĩ sẽ rất có lợi nếu mọi người viết mã với sự hiểu biết mã đó sẽ làm gì và không bỏ qua bất kỳ câu hỏi nào dẫn đến ký hiệu O (x) là "tối ưu hóa". Có rất nhiều mã bạn có thể viết ngay bây giờ và với một chút suy nghĩ về hiệu suất, tránh 80% các vấn đề.

Cũng xem xét rằng rất nhiều vấn đề về hiệu suất sẽ không xảy ra trong môi trường của bạn và không ngay lập tức. Đôi khi, bạn sẽ có một khách hàng đẩy giới hạn hoặc nhà phát triển khác quyết định xây dựng trên khung của bạn và tăng số lượng đối tượng lên gấp 10 lần. Với một số mặc dù về hiệu suất bây giờ, bạn có thể tránh thiết kế lại rất tốn kém sau này. Và nếu vấn đề được tìm thấy sau khi phần mềm được phát hành chính thức, ngay cả một sửa chữa đơn giản cũng trở nên đắt hơn gấp 20 lần khi áp dụng.

Vì vậy, trong kết luận, giữ cho hiệu suất trong tâm trí mọi lúc giúp phát triển các thói quen tốt. Điều này cũng quan trọng để có được viết sạch, đơn giản nhất có thể và mã có tổ chức.


+1: Đây là một trong những yếu tố việc làm cho những người được thuê để phát triển phần mềm Shrinkwrap và các trang web thương mại . Phòng ngừa ít tốn kém hơn lời nguyền của khách hàng.
rwong

6

Tôi nghi ngờ rằng rất nhiều thứ bạn đang thấy là lỗi lấy mẫu đơn giản. Khi mọi người đang xử lý các tình huống đơn giản, họ viết mã và đó là kết thúc của mọi thứ. Họ đặt câu hỏi khi họ xử lý một vấn đề tương đối khó khăn, chẳng hạn như cần tối ưu hóa, đặc biệt là trong tình huống không nhất thiết phải tối ưu hóa.

Điều đó nói rằng, chắc chắn cũng có một số tối ưu hóa sớm có liên quan. Chính xác hay nói cách khác, C và C ++ nổi tiếng về hiệu suất, có xu hướng thu hút những người quan tâm đến hiệu suất - bao gồm cả những người có thể tối ưu hóa nhiều để thưởng thức vì nó thực sự cần thiết.


1
+1 - Lưu ý: Hầu hết các câu hỏi SO có thẻ "hiệu suất" có thể là một phần của lỗi lấy mẫu đó: P
Billy ONeal

3
Tôi chắc chắn thấy rất nhiều câu hỏi tối ưu hóa sớm ở đây ... Tôi nghĩ rằng xuất phát từ thực tế là rất nhiều lập trình viên có sở thích bắt đầu với ý tưởng viết trò chơi, và có một lượng lớn sách "tối ưu hóa" vô nghĩa và các trang web liên quan đến phát triển trò chơi đưa ý tưởng tồi vào đầu người mới bắt đầu. :-)
R ..

4
Khi bạn đang đối phó với một điều gì đó khó khăn, thường thì việc giải quyết vấn đề rắc rối sẽ dễ dàng hơn và tránh xa thời gian của bạn để lo lắng về việc bạn nên sử dụng i++hay++i
Carson63000

@ Carson63000: có mà hoàn toàn có thể làm lệch các mẫu. Hoặc họ dành thời gian trả lời các câu hỏi về lý do tại sao tôi operator ++không biên dịch.
rwong

4

Một vài câu trả lời khác đề cập đến các hệ thống nhúng và tôi muốn mở rộng về điều này.

Có rất nhiều thiết bị chứa bộ xử lý cấp thấp, ví dụ: bộ điều khiển nồi hơi trong nhà bạn, hoặc một máy tính bỏ túi đơn giản, hoặc hàng tá chip bên trong một chiếc xe hơi hiện đại.

Để tiết kiệm tiền, chúng có thể có số lượng đèn flash (để lưu trữ mã) và RAM có vẻ nhỏ đối với những người chỉ viết mã cho PC hoặc điện thoại thông minh. Để tiết kiệm năng lượng, chúng có thể chạy ở tốc độ xung nhịp tương đối thấp.

Lấy một ví dụ, họ vi điều khiển STM32 đi từ 24 MHz, flash 16 KB và RAM 4 KB , lên đến 120 MHz, flash 1 MB và RAM 128 KB .

Khi viết mã cho các chip như thế này, sẽ tiết kiệm rất nhiều thời gian nếu bạn đặt mục tiêu làm cho mã của mình hiệu quả nhất có thể như một vấn đề tất nhiên. Rõ ràng, tối ưu hóa sớm vẫn là một ý tưởng tồi; nhưng với thực tế, bạn tìm hiểu làm thế nào các vấn đề phổ biến có thể được giải quyết nhanh chóng và / hoặc với các tài nguyên tối thiểu và mã phù hợp.


1
điểm tốt để xem xét cho các hệ thống nhúng, một lĩnh vực tôi làm việc trong bản thân mình. Ngay cả với suy nghĩ đó, kinh nghiệm của tôi trong nhiều năm qua là tối ưu hóa sai lầm luôn là một sự lãng phí thời gian. Không có công cụ để hướng dẫn chúng tôi, chúng tôi hiếm khi tìm thấy các khu vực có vấn đề
Jeff

2

Những việc cơ bản ngôn ngữ ở mức độ thấp, khi một người chạy vào một trường hợp hiệu suất bệnh lý nơi một chi tiết đó sẽ không thành vấn đề 99% thời gian đang gây ra những nút cổ chai, người ta thực sự có cơ hội để làm việc trực tiếp xung quanh vấn đề này (không giống với hầu hết các khác ngôn ngữ); nhưng tất nhiên, thông thường, làm thế nào để làm như vậy một cách hiệu quả nhất là không rõ ràng ngay lập tức. Do đó một nửa trong số các câu hỏi tối ưu hóa kỳ lạ / thú vị được hỏi ở đây.

Nửa còn lại đến từ những người tò mò về việc họ có thể đến gần kim loại đến mức nào. Chúng thực chất là những ngôn ngữ cấp thấp, sau tất cả ...


+1: đáng để chỉ ra rằng "hiệu suất bệnh lý" có thể xảy ra với bất kỳ ai trên thế giới, bất kể ngôn ngữ hoặc nền tảng. Khả năng triển khai lại bằng ngôn ngữ cấp thấp hơn để kiểm tra và đọc phân tách có thể cung cấp nhiều thông tin chi tiết hơn , nhưng không phải lúc nào cũng cung cấp giải pháp khả thi. Ví dụ: "Tôi biết tôi có thể thực hiện việc lắp ráp - nhưng nó cần chạy trong môi trường tin cậy một phần!"
rwong

2

Hiệu suất luôn là một chủ đề nóng khi bạn giao dịch với C và C ++. Về việc một người nên đi bao xa, bạn luôn có thể phát điên đến mức ASM nội tuyến, hoặc sử dụng số học con trỏ để lặp lại nhanh hơn. Tuy nhiên, có một điểm mà người ta dành quá nhiều thời gian để tối ưu hóa rằng việc phát triển chương trình tổng thể bị đình trệ.

Khi xử lý các vấn đề này, có hiệu suất lập trình viên và hiệu suất mã. Mà trong số này để tập trung vào sẽ luôn đưa ra những câu hỏi thú vị. Cuối cùng, câu hỏi quan trọng nhất là nó đáng chú ý như thế nào đối với người dùng. Người dùng sẽ làm việc với dữ liệu tạo ra các mảng với hàng trăm hoặc hàng nghìn phần tử? Trong trường hợp này, mã hóa để hoàn thành công việc nhanh chóng có thể khiến người dùng của bạn phàn nàn rằng các hoạt động tiêu chuẩn của chương trình chậm.

Sau đó, có người dùng sẽ làm việc với một lượng nhỏ dữ liệu. Một vài tệp ở đây và ở đó, nơi thực hiện những việc như sắp xếp và thao tác tệp sẽ không được người dùng chú ý nếu bạn đang sử dụng các hàm cấp cao hơn giúp bạn dễ dàng duy trì chi phí của một số hiệu suất.

Đây chỉ là một ví dụ nhỏ về các vấn đề bạn sẽ gặp phải. Các vấn đề khác bao gồm phần cứng của người dùng mục tiêu. Bạn sẽ phải lo lắng về hiệu năng hơn rất nhiều nếu bạn xử lý các hệ thống nhúng, sau đó nếu người dùng của bạn có, ví dụ, các máy lõi kép có hợp đồng ram.


Hmm .. Tôi không sử dụng số học con trỏ để lặp lại nhanh hơn - đó là phép nhân và thêm hướng dẫn cho mỗi vòng lặp cho dù bạn đang sử dụng phép lặp dựa trên chỉ mục hay dựa trên con trỏ. Tôi sử dụng nó mặc dù, bởi vì nó thường rõ ràng hơn lặp đi lặp lại dựa trên chỉ mục.
Billy ONeal

số học con trỏ không nhanh hơn w / e.

2

Tại sao các lập trình viên quan tâm rất nhiều? Có những ý tưởng ngớ ngẩn tập trung vào đầu họ, chẳng hạn như giải quyết các vấn đề về hiệu suất trước khi họ biết họ có chúng, và không hiểu khi họ đoán .

Điều đó thật khó khăn bởi vì, theo kinh nghiệm của tôi, có một số vấn đề về hiệu suất mà người ta nên nghĩ đến trước thời hạn. Phải có kinh nghiệm để biết những gì họ đang có.

Điều đó nói rằng, phương pháp tôi sử dụng tương tự như của bạn, nhưng không giống nhau:

  1. Bắt đầu với thiết kế đơn giản nhất có thể. Đặc biệt, cấu trúc dữ liệu nên được chuẩn hóa và tối thiểu nhất có thể. Trong phạm vi nó có sự dư thừa không thể tránh khỏi, người ta nên ngại ngùng thông báo như một cách để giữ cho nó nhất quán. Tốt hơn là chịu đựng sự không nhất quán tạm thời, và sửa chữa nó với một quy trình định kỳ.

  2. Khi chương trình đang được phát triển, hãy thực hiện điều chỉnh hiệu suất định kỳ, bởi vì các vấn đề về hiệu năng có một cách lặng lẽ len vào. Phương pháp tôi sử dụng là tạm dừng ngẫu nhiên , vì tôi nghĩ đó là cách tốt nhất.

Đây là một ví dụ điển hình về những gì tôi muốn nói.


1

Thành thật mà nói, nó phụ thuộc vào mục tiêu của bạn là gì và bạn đang lập trình chuyên nghiệp hay theo sở thích.

Ngày nay, máy tính hiện đại là những cỗ máy thực sự mạnh mẽ. Bất kể hoạt động cơ bản nào bạn quyết định thực hiện, cho dù bạn đang cố gắng tối ưu hóa vi mô hay không, họ có thể thực hiện công việc của mình nhanh chóng đáng kể. Nhưng tất nhiên, nếu bạn đang làm một cái gì đó khác (ví dụ, siêu máy tính cho các lĩnh vực như vật lý hoặc hóa học), bạn có thể muốn tối ưu hóa nhiều như bạn muốn.

Các lập trình viên MIT đầu tiên không được sinh ra để tạo ra những thứ tuyệt vời; Họ bắt đầu đơn giản hóa và cung cấp năng lượng cho các thuật toán hiện có. Niềm tự hào của họ là làm cho 2 + 2 cung cấp ít hơn bốn trong hai giây so với thuật toán hiện có (đó chỉ là một ví dụ, bạn hiểu ý). Họ liên tục cố gắng sử dụng ít thẻ đục lỗ trong máy TI-83 để thực hiện.

Ngoài ra, nếu bạn đang lập trình cho các hệ thống nhúng, thì bạn chắc chắn phải để mắt đến hiệu suất vi mô. Bạn không muốn có đồng hồ kỹ thuật số chậm, đánh dấu 5 giây thứ hai sớm hơn so với đồng hồ kỹ thuật số khác.

Cuối cùng, nếu bạn là một lập trình viên có sở thích thì chắc chắn không có hại gì trong việc tối ưu hóa các chi tiết nhỏ nhất mặc dù chương trình của bạn rất nhanh. Nó không cần thiết, nhưng chắc chắn một cái gì đó bạn có thể làm việc và có cơ hội tìm hiểu thêm. Nếu bạn đang làm việc chuyên nghiệp trong một phần mềm, bạn không thể sử dụng thứ xa xỉ đó trừ khi nó cực kỳ cần thiết.


1
Tôi không nghĩ là một lập trình viên có sở thích có liên quan đến điều này. Chỉ vì bạn không làm một cái gì đó một cách chuyên nghiệp không nhất thiết có nghĩa là bạn có toàn bộ thời gian trên thế giới để dành cho nó. Hơn nữa, hầu hết những người có sở thích sẽ phạm sai lầm lớn hơn, chẳng hạn như chọn sai thuật toán, hơn hầu hết các chuyên gia thực sự sẽ mắc phải. Hơn nữa, chuyên gia có thể đang làm việc trên một sản phẩm xử lý dữ liệu nhiều hơn đáng kể so với người có sở thích (do đó phải nhanh hơn) và phải khiến khách hàng hài lòng với hiệu suất của ứng dụng. Người chơi không có những ràng buộc như vậy.
Billy ONeal

Họ không, nhưng họ chắc chắn có nhiều thời gian hơn để làm việc với họ chỉ khi họ muốn.

3
Tôi sẽ tranh luận ngược lại. Tôi đã có 8 giờ hoặc hơn một ngày để làm việc gì đó như một chuyên gia. Tôi nhận được 1, có thể 2 giờ mỗi ngày cho các dự án sở thích của tôi.
Billy ONeal

1

sử dụng thuật toán O (N2) so với O (NlogN) trên danh sách 100 mục.

Tôi đã ở trong một tình huống tương tự gần đây. Tôi đã có một loạt các mặt hàng. Trong trường hợp dự kiến, có hai mục (!) Trong danh sách và ngay cả trong trường hợp xấu nhất tôi cũng không mong đợi nhiều hơn bốn hoặc có thể tám.

Tôi cần sắp xếp danh sách đó. Hóa ra, thay thế std::sortbằng một mạng sắp xếp (về cơ bản là rất nhiều ifs lồng nhau ) đã loại bỏ một tỷ lệ lớn thời gian chạy (tôi không nhớ số đó nhưng nó giống như 10 102020%). Đây là một lợi ích rất lớn của việc tối ưu hóa vi mô và mã này hoàn toàn quan trọng về hiệu năng.

Tất nhiên, tôi chỉ làm điều này sau khi hồ sơ. Nhưng vấn đề là, nếu tôi sử dụng một ngôn ngữ bất tiện và phức tạp như C ++ (chưa kể các quy tắc phức tạp vô cùng phức tạp của nó để giải quyết quá tải), thì tôi muốn gặt hái đầy đủ lợi ích.


1

Sử dụng năng lượng tích lũy

Có một câu trả lời mà tôi luôn nghĩ là thiếu trong các cuộc thảo luận này và điều đó làm phiền tôi một chút - sử dụng năng lượng tích lũy .

Chắc chắn, có lẽ sẽ không có vấn đề gì nếu bạn viết chương trình của mình bằng ngôn ngữ được dịch ở mức độ cao và để nó chạy trong trình duyệt với một vài lớp cảm ứng hoặc nếu vòng lặp của bạn mất 0,01 giây thay vì 0,001 giây. Không ai sẽ nhận thấy, đó là, không có người dùng cá nhân sẽ nhận thấy.

Nhưng khi hàng chục ngàn, thậm chí hàng triệu người dùng trong một số trường hợp sử dụng mã của bạn, tất cả những gì không hiệu quả sẽ tăng thêm. Nếu công cụ của bạn ngăn CPU đi vào trạng thái ngủ chỉ mười giây mỗi ngày và một triệu người dùng sử dụng nó, thuật toán không hiệu quả của bạn chỉ sử dụng thêm 140 kWh [1] mỗi ngày.

Tôi hiếm khi thấy điều này được thảo luận, và tôi nghĩ điều đó thật đáng buồn. Tôi nghi ngờ rằng các số liệu còn tồi tệ hơn nhiều đối với các khung công tác phổ biến, như Firefox và các ứng dụng web tương tác lạ mắt, và sẽ rất thú vị khi nghiên cứu.


[1] Tôi mới thực hiện được điều đó, 10 triệu giây gấp 50 Watts. Con số chính xác phụ thuộc vào nhiều thứ.


1
Bạn nên bắt đầu ngay bằng cách đề cập đến từ ma thuật "di động". Khi chạy trên nền tảng máy tính để bàn, một ứng dụng cần 1/100 giây thời gian CPU để vẽ khung 60 lần một giây sẽ "đủ nhanh"; cải thiện hiệu suất gấp mười lần sẽ tạo ra sự khác biệt cho người dùng. Tuy nhiên, trên nền tảng di động, một ứng dụng chạy với mức sử dụng CPU 90% có thể tiêu hao pin nhanh hơn nhiều so với chạy ở mức 10%.
supercat

1

Đôi khi bạn chỉ có các thuật toán không thể tốt hơn thời gian tuyến tính mà vẫn có nhu cầu hiệu năng cao.

Một ví dụ là xử lý video trong đó bạn không thể làm cho hình ảnh / khung hình sáng hơn như một ví dụ cơ bản mà không lặp qua từng pixel (vâng, tôi cho rằng bạn có thể với một số loại cấu trúc phân cấp biểu thị các thuộc tính được thừa hưởng bởi trẻ em cuối cùng rơi xuống các ô hình ảnh đối với các nút lá, nhưng sau đó bạn sẽ trì hoãn chi phí vòng lặp cao hơn qua từng pixel đến trình kết xuất và mã có thể khó duy trì hơn cả bộ lọc hình ảnh được tối ưu hóa vi mô nhất).

Có rất nhiều trường hợp như thế trong lĩnh vực của tôi. Tôi có xu hướng thực hiện nhiều vòng lặp phức tạp tuyến tính hơn mà phải chạm vào mọi thứ hoặc đọc mọi thứ so với các vòng lặp có lợi từ bất kỳ loại cấu trúc dữ liệu hoặc thuật toán phức tạp nào. Không có công việc nào có thể bỏ qua khi mọi thứ phải được chạm vào. Vì vậy, tại thời điểm đó nếu bạn chắc chắn phải đối phó với sự phức tạp tuyến tính, bạn phải làm cho công việc được thực hiện mỗi lần lặp rẻ hơn và rẻ hơn.

Vì vậy, trong trường hợp của tôi, tối ưu hóa quan trọng và phổ biến nhất thường là biểu diễn dữ liệu và bố trí bộ nhớ, đa luồng và SIMD (thường theo thứ tự này với biểu diễn dữ liệu là quan trọng nhất, vì nó ảnh hưởng đến khả năng thực hiện hai điều sau). Tôi không gặp phải quá nhiều vấn đề được giải quyết bằng cây, bảng băm, thuật toán sắp xếp và những thứ tương tự. Mã hàng ngày của tôi là nhiều hơn trong " , cho mỗi điều, làm một cái gì đó."

Tất nhiên đó là một trường hợp khác để nói về khi tối ưu hóa là cần thiết (và quan trọng hơn, khi chúng không), vi mô hoặc thuật toán. Nhưng trong trường hợp cụ thể của tôi, nếu đường dẫn thực thi quan trọng cần tối ưu hóa, mức tăng tốc độ 10 + thường đạt được bằng cách tối ưu hóa ở cấp độ vi mô như đa luồng, SIMD và sắp xếp lại bố cục bộ nhớ và các mẫu truy cập để cải thiện vị trí tham chiếu. Tôi không thường xuyên nói rằng, thay thế một loại bong bóng bằng một loại introort hoặc một loại radix hoặc phát hiện va chạm phức tạp bậc hai với một BVH nhiều như tìm các điểm nóng, ví dụ, được hưởng lợi từ việc tách trường nóng / lạnh.

Bây giờ trong trường hợp của tôi, lĩnh vực của tôi rất quan trọng về hiệu năng (raytracing, động cơ vật lý, v.v.) rằng một raytracer chậm nhưng hoàn toàn chính xác, mất 10 giờ để hiển thị một hình ảnh thường được coi là vô dụng hoặc hơn một cách nhanh chóng hoàn toàn tương tác nhưng xuất ra những hình ảnh xấu nhất với các tia bị rò rỉ ở khắp mọi nơi do thiếu giao thoa tia nước / tri. Tốc độ được cho là chỉ số chất lượng chính của phần mềm như vậy, thậm chí còn hơn cả tính chính xác ở một số điểm (vì "tính chính xác" là một ý tưởng mờ nhạt với raytracing vì mọi thứ đều gần đúng, miễn là nó không bị hỏng hoặc bất cứ điều gì tương tự). Và khi đó, nếu tôi không nghĩ về việc trả trước hiệu quả, tôi thấy tôi phải thực sự thay đổi mã ở mức thiết kế đắt nhất để xử lý các thiết kế hiệu quả hơn. Vì vậy, nếu tôi không '

Chơi game là một lĩnh vực tương tự như của tôi. Không quan trọng logic logic của trò chơi của bạn như thế nào hoặc khả năng duy trì và thiết kế tuyệt vời của cơ sở mã của bạn như thế nào nếu trò chơi của bạn chạy ở tốc độ 1 khung hình mỗi giây như trình chiếu. Trong một số trường nhất định, việc thiếu tốc độ thực sự có thể khiến ứng dụng trở nên vô dụng đối với người dùng. Không giống như các trò chơi, không có số liệu "đủ tốt" trong các lĩnh vực như raytracing. Người dùng luôn muốn có nhiều tốc độ hơn và cạnh tranh công nghiệp chủ yếu là tìm kiếm các giải pháp nhanh hơn. Nó sẽ không bao giờ đủ tốt cho đến thời gian thực, tại thời điểm đó, các trò chơi sẽ sử dụng các công cụ theo dõi đường dẫn. Và sau đó có lẽ nó vẫn chưa đủ tốt cho VFX, kể từ đó, các nghệ sĩ có thể muốn tải hàng tỷ đa giác và có các mô phỏng hạt với sự tự va chạm giữa hàng tỷ hạt ở tốc độ 30+ FPS.

Bây giờ nếu nó thoải mái, mặc dù tôi vẫn viết khoảng 90% mã bằng ngôn ngữ kịch bản (Lua) mà không phải lo lắng về hiệu suất. Nhưng tôi có một số lượng mã lớn bất thường thực sự cần phải lặp qua hàng triệu đến hàng tỷ thứ và khi bạn lặp qua hàng triệu đến hàng tỷ thứ, bạn bắt đầu nhận thấy sự khác biệt sử thi giữa mã đơn luồng ngây thơ đó gọi một lỗi bộ nhớ cache với mỗi lần lặp so với nói, mã vectơ chạy song song truy cập các khối liền kề trong đó không có dữ liệu không liên quan được tải vào một dòng bộ đệm.


0

Như bạn đã đề cập, quan tâm đến các vấn đề về hiệu suất vi mô là vô ích trước khi bạn tính đến một số vấn đề thực sự gây ra bởi những vấn đề này


0

Nói chung là không thể trả lời câu hỏi này. Hầu hết các phần mềm đang được xây dựng ngày nay là các trang web nội bộ và các ứng dụng LOB, và đối với kiểu lập trình đó, lý luận của bạn là hoàn toàn chính xác. Mặt khác, nếu bạn đang viết một cái gì đó như trình điều khiển thiết bị hoặc công cụ trò chơi, thì không có tối ưu hóa nào là "quá sớm"; rất có thể phần mềm của bạn sẽ chạy trên các hệ thống rất khác nhau với các ràng buộc phần cứng khác nhau. Trong trường hợp đó, bạn nên thiết kế để thực hiện và đảm bảo rằng bạn không chọn thuật toán tối ưu phụ.


Chính xác những gì tôi muốn nói. Mỗi phần mềm đều có miền ứng dụng của nó và không được mong đợi sẽ hoạt động tối ưu bên ngoài phần mềm. Theo nghĩa này, tối ưu hóa sớm là một ví dụ về chủ nghĩa hoàn hảo sai lầm.
K.Steff

0

Tôi nghĩ vấn đề của lập trình viên, người quan tâm rất nhiều đến hiệu suất là, đôi khi trong đời, anh ta cần viết mã trình diễn vi mô, có thể rất khẩn trương, và anh ta đã học, đã học, đã học, và cuối cùng anh ta biết rất nhiều điều và thủ đoạn.

Và bây giờ thật khó để quên, và không có sự đo lường trước đó, điều đó cho thấy, anh ta không cần phải lo lắng, anh ta ở bên an toàn, sử dụng mã nhanh.

Thật là tốt khi thể hiện kiến ​​thức sâu sắc, kỹ năng của bạn và một số thủ thuật, và sử dụng lại những gì bạn đã học. Nó làm cho bạn cảm thấy có giá trị, và thời gian, dành cho việc học, là giá trị.

Đôi khi trong cuộc sống của tôi, tôi đã học tăng tiền tố nhanh hơn ...

for (int i = 0; i < MAX; ++i)

... so với gia tăng postfix:

for (int i = 0; i < MAX; i++)

Bây giờ nếu MAX thấp, điều đó sẽ không thành vấn đề và nếu có công việc thực sự trong vòng lặp, điều đó cũng không thành vấn đề. Nhưng không có lý do để sử dụng phiên bản postfix, ngay cả khi trình biên dịch ngày nay tự tối ưu hóa mã.

Có thể những người tìm kiếm hiệu suất cần một mục tiêu bổ sung, bên cạnh việc viết 'mã làm việc', như 'mã làm việc và có thể đọc được' để có một hướng dẫn trong biển lớn các tùy chọn.


0

Có phải tôi đã đủ may mắn để không phải lo lắng quá nhiều về nó, hay tôi là một lập trình viên tồi?

Bạn có quan tâm đến yêu cầu của bạn? Nếu hiệu suất không phải là một yêu cầu thì đừng lo lắng về nó. Dành bất kỳ thời gian đáng kể nào cho nó là một sự bất lợi cho chủ nhân của bạn.

Trong một phạm vi hiệu suất luôn luôn là một yêu cầu. Nếu bạn có thể đánh nó mà không nghĩ về nó, bạn có lý khi không nghĩ về nó.

Cá nhân, tôi thường bị thúc đẩy bởi hiệu suất khi các bài kiểm tra của tôi mất nhiều thời gian để vượt qua. Tôi quá nôn nóng để chờ 5 phút trong khi một loạt các bài kiểm tra vượt qua. Nhưng điều đó thường được giải quyết bằng cách đấu tranh với các bài kiểm tra.

Câu hỏi của tôi là tại sao một số lượng lớn các lập trình viên quan tâm rất nhiều? Có thực sự là một vấn đề cho hầu hết các nhà phát triển,

Có một số lượng lớn các lập trình viên được chứng minh là họ quan tâm đến mức nào. Có số lượng lớn người không. Hãy nói về những người không.

Một trong những điều đầu tiên các lập trình viên học ở trường, sau khi làm thế nào để mọi thứ thực sự hoạt động, là ký hiệu O lớn. Nhiều người trong số họ học bài học đúng cách và do đó tập trung đúng vào những thứ bị ảnh hưởng đáng kể bởi n. Những người khác không có được toán học và chỉ lấy đi bài học mà một khi nó hoạt động cần phải nhanh. Tồi tệ hơn, một số trong những sinh viên này không bao giờ học bất cứ điều gì khác về những gì quan trọng phải làm với mã của bạn ngoài việc làm cho nó hoạt động và làm cho nó hoạt động nhanh. Các bài học bị bỏ lỡ: làm cho nó dễ đọc, thiết kế tốt, không chơi xung quanh nó mà không có lý do.

Knuth đã đúng: tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Nhưng một khi nó hoạt động thì bước tiếp theo là gì? Nhanh phải không? KHÔNG! Bước tiếp theo là có thể đọc được. Có thể đọc là bước đầu tiên, tiếp theo, giữa và cuối cùng. Nhiều người tôi thấy thực hiện tối ưu hóa hiệu suất không cần thiết đang ném khả năng đọc dưới xe buýt.

Một số thậm chí còn nhận được một sự hồi hộp đồi bại từ việc mã của họ không thể đọc được. Họ đã phải chịu đựng khi nhìn vào mã khó hiểu do người khác tạo ra nên giờ đến lượt họ hoàn vốn.

Tôi biết điều này bởi vì tôi đã từng làm điều này. Tôi đã từng tái cấu trúc một dòng 5 hoàn toàn có thể đọc được nếu cấu trúc xuống một biểu thức boolean một dòng không thể mã hóa và tự hào gửi nó cho giáo sư của tôi để mong gây ấn tượng vì tôi có thể tạo ra thứ gì đó nhỏ gọn và đáng sợ. Tôi đã không nhận được lời khen ngợi mà tôi đã hy vọng.

Nếu mã vẫn có thể đọc được, làm cho nó nhanh hơn sau đó là dễ dàng. Đó là lý do tại sao Knuth nhấn mạnh "sớm" không "không cần thiết". Bởi vì chắc chắn, nhanh hơn là tốt hơn. Nhưng tốt hơn chỉ tốt hơn tùy thuộc vào những gì bạn hy sinh cho nó. Vì vậy, hãy đợi cho đến khi bạn biết hiệu suất nào bạn thực sự cần trước khi bạn hy sinh cho nó. Hy sinh khả năng sẵn sàng miễn cưỡng vì một khi nó đã biến mất, thật khó để lấy lại.

Ngoài khả năng đọc là toàn bộ thế giới của thiết kế phần mềm. Những gì trang web này là về. Một số không có manh mối gì để làm như thiết kế. Vì vậy, vì họ không thể gây ấn tượng với thiết kế nên họ tạo ra một mớ hỗn độn không thể giải mã được để mọi người không thể nói rằng họ không có đầu mối. Vì không ai sửa mã của họ nên nó phải là mã tốt phải không?

Đối với một số người, hiệu suất là tất cả lý do để làm bất cứ điều gì họ muốn. Lập trình viên có rất nhiều quyền lực và tự chủ. Niềm tin đã được đặt vào họ. Đừng lạm dụng lòng tin.

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.