Làm thế nào để đo lường sự phức tạp trong thực tế trong dự án phần mềm lớn của bạn?


11

Trong trường đại học, tại các khóa học thuật toán của chúng tôi, chúng tôi học cách tính toán chính xác độ phức tạp của các thuật toán đơn giản khác nhau được sử dụng trong thực tế, chẳng hạn như bảng băm hoặc sắp xếp nhanh.

Nhưng bây giờ trong một dự án phần mềm lớn, khi chúng tôi muốn làm cho nó nhanh hơn, tất cả những gì chúng tôi làm là xem xét từng phần riêng lẻ - một vài vòng lặp lồng nhau có thể được thay thế bằng bảng băm nhanh hơn, một tìm kiếm chậm ở đây có thể được tăng tốc một kỹ thuật lạ mắt hơn - nhưng chúng tôi không bao giờ tính toán độ phức tạp của toàn bộ đường ống của chúng tôi.

Có cách nào để làm điều đó? Hay những người trong thực tế chỉ dựa vào "cục bộ" bằng thuật toán nhanh, để làm cho toàn bộ ứng dụng nhanh hơn, thay vì toàn cầu xem xét toàn bộ ứng dụng?

(Bởi vì dường như tôi không cần thiết phải chỉ ra rằng nếu bạn tích lũy được một số lượng lớn thuật toán được biết là rất nhanh, thì bạn cũng sẽ có một ứng dụng nhanh nói chung.)

Tôi đang hỏi điều này, bởi vì tôi được giao nhiệm vụ tăng tốc một dự án lớn mà người khác đã viết, trong đó rất nhiều thuật toán đang tương tác và làm việc với dữ liệu đầu vào, vì vậy tôi không rõ tác động của việc tạo ra thuật toán đơn lẻ nhanh hơn như thế nào toàn bộ ứng dụng.


1) Điều này đòi hỏi một phương pháp thử nghiệm, để tìm ra những điểm cần cải thiện. Kiểm tra điểm chuẩn, Kiểm tra độ bền, Kiểm tra động (bằng cách theo dõi số liệu bộ nhớ / CPU của từng thành phần). 2) Sau khi tìm thấy những điểm cần cải thiện, bạn sẽ tìm ra nguyên nhân gốc rễ cho những điểm đó. 3) Tìm giải pháp để giải quyết nguyên nhân gốc rễ, duy trì tính đúng đắn.
trao đổi quá mức vào

bạn cần một số công cụ cho các thử nghiệm này được đề cập trong điểm 1
trao đổi quá mức vào

1
Phân tích Big O không cho bạn biết thuật toán sẽ thực hiện như thế nào. Nó cho bạn biết hiệu suất sẽ mở rộng như thế nào , khi ntăng lên.
John Wu

Câu trả lời:


5

Các dự án phần mềm lớn bao gồm nhiều thành phần khác nhau và không phải tất cả chúng đều là nút cổ chai. Hoàn toàn ngược lại: đối với hầu hết mọi chương trình đã thấy trong cuộc sống của tôi, nơi hiệu suất thấp là một vấn đề, nguyên tắc Pareto được áp dụng: hơn 80% mức tăng hiệu suất có thể đạt được bằng cách tối ưu hóa ít hơn 20% mã (trong thực tế, tôi nghĩ rằng con số thường cao hơn 95% đến 5%).

Vì vậy, bắt đầu nhìn vào các mảnh riêng lẻ thường là cách tiếp cận tốt nhất. Đó là lý do tại sao hồ sơ (như được giải thích trong câu trả lời của David Arno ) là tốt, vì nó giúp bạn xác định 5% mã được đề cập trong đó việc tối ưu hóa sẽ mang lại cho bạn "cú hích lớn nhất". Tối ưu hóa "toàn bộ ứng dụng" có nguy cơ áp đảo nhất định và nếu bạn tối ưu hóa 95% đó ngay cả với hệ số 10, thì nó thường không có tác dụng có thể đo lường được. Cũng lưu ý rằng cấu hình cho bạn biết nhiều hơn bất kỳ ước tính độ phức tạp thuật toán giả định nào, vì thuật toán đơn giản cần các bước O (N ^ 3) vẫn có thể nhanh hơn thuật toán phức tạp yêu cầu O (N log (N)) miễn là N là đủ nhỏ

Sau khi hồ sơ tiết lộ các điểm nóng, người ta có thể tối ưu hóa chúng. Tất nhiên, một "điểm nóng" có thể lớn hơn chỉ một hoặc hai dòng mã, đôi khi người ta phải thay thế toàn bộ một thành phần để làm cho nó nhanh hơn, nhưng đó thường sẽ chỉ là một phần nhỏ của cơ sở mã trong một chương trình lớn hơn .

Các kỹ thuật tối ưu hóa điển hình bao gồm

  • cải thiện việc sử dụng các thuật toán và cấu trúc dữ liệu

  • tinh chỉnh trước đây

  • tối ưu hóa vi mô tại một số điểm nóng thực sự

  • mã hóa các phần quan trọng bằng mã lắp ráp hoặc CUDA

Lưu ý rằng các kỹ thuật này đang hoạt động ở các mức độ trừu tượng khác nhau, một số trong số chúng xem một thành phần "toàn bộ" hơn các kỹ thuật khác. Vì vậy, nó phụ thuộc vào ý của bạn bởi "tất cả những gì chúng tôi làm là nhìn vào từng mảnh riêng lẻ" - nếu bạn chỉ có tối ưu hóa vi mô trong tâm trí, tôi không đồng ý rằng "chúng tôi" chỉ làm việc trên đó. Nhưng nếu bạn có nghĩa là áp dụng những tối ưu hóa toàn diện đó cho các bộ phận hoặc bộ phận bị cô lập, thì "chúng tôi" có thể đang làm việc trên các bộ phận phù hợp và bạn nên đặt câu hỏi về sự mong đợi của mình.


13

Cách tiêu chuẩn, đã thử và thử nghiệm là lập hồ sơ mã . Bạn thực hiện phân tích động của hệ thống đang chạy để đo thời gian, sử dụng bộ nhớ, vv Sau đó phân tích kết quả để tìm ra tắc nghẽn hiệu suất.

Những nút thắt đó sau đó được viết lại bằng thực nghiệm và kết quả được lập lại một lần nữa để xác định rằng việc tăng tốc độ, giảm sử dụng bộ nhớ, v.v. đã đạt được. Quá trình này sau đó được lặp lại cho đến khi đạt được hiệu suất đạt được.


1
Điều này giải quyết vấn đề hiệu suất, đó là lý do tại sao chúng tôi làm điều đó, nhưng không trả lời câu hỏi ban đầu. Tôi nghĩ rằng trong trường hợp xấu nhất về thời gian hoặc độ phức tạp không gian tốt nhất sẽ được tìm ra bằng cách sử dụng một công cụ phân tích chương trình tĩnh, có thể bị thiếu. Kiểm tra hiệu suất rất tốt cho các tình huống cụ thể, nhưng chúng không cho bạn biết nhiều về các tình huống xấu nhất có thể xảy ra.
Frank Hileman

3
@FrankHileman Tôi nghĩ vấn đề ở đây là hiệu suất là mối quan tâm thực tế và chỉ có thể được đo lường thực tế. Bạn không sử dụng toán học để tìm ra nút cổ chai của phần mềm, ngay cả khi bạn có thể giải quyết nó một khi được tìm thấy bằng toán học (thuật toán).
tự đại diện

Trên một lưu ý liên quan, trong bài thuyết trình trình chiếu thời gian cũ (slide thủy tinh), có một công nghệ hoàn toàn giả vờ về cách tính toán mật độ trung bình của một chiếc đèn lồng một cách toán học để xác định độ sáng của ánh sáng sử dụng. Hoàn toàn vô dụng: nếu hình ảnh không hiển thị tốt, bạn sẽ có được ánh sáng rực rỡ hơn!
tự đại diện

@Wildcard Mặc dù hiệu suất chỉ có thể được đo khi chạy, nhưng nó có thể được dự đoán tĩnh. Một lựa chọn kém về cấu trúc dữ liệu có thể trông ổn, hiệu năng khôn ngoan, trong các thử nghiệm hiệu năng, nhưng thất bại thảm hại trong các trường hợp cạnh có thể được dự đoán trong phân tích tĩnh. Đây là cùng một lý do chúng tôi xem xét các phân tích phức tạp trường hợp xấu nhất cho các cấu trúc dữ liệu nói chung.
Frank Hileman

@Wildcard: bạn đúng, tuy nhiên Frank cũng rất đúng khi bài này không trả lời câu hỏi.
Doc Brown

3

Mặc dù các câu trả lời khác là chính xác và cung cấp một số hướng dẫn, tôi nghĩ rằng họ bỏ lỡ một bước. Trong một hệ thống phức tạp như bạn đang làm việc với bây giờ, hiểu các thành phần khác nhau tạo nên hệ thống là chìa khóa để hiểu tại sao một cái gì đó chậm.

Bước đầu tiên của tôi là bắt tay vào sơ đồ kiến ​​trúc chi tiết hoặc tự tạo một sơ đồ. Chỉ ra các bước được thực hiện bởi những thành phần nào trong phần mềm và mỗi bước mất bao lâu.

Ngoài ra, tìm hiểu làm thế nào các thành phần tương tác với nhau. Điều này có thể làm cho tất cả sự khác biệt.

Ví dụ: tôi đã thấy mã trong C # trong đó giao diện giữa hai thành phần đang vượt qua một IEnumerable được xây dựng bởi thành phần đầu tiên, sau đó được liệt kê bởi thành phần thứ hai. Trong C #, điều này đòi hỏi phải chuyển đổi ngữ cảnh, có thể tốn kém trong một số trường hợp nhất định. Giải quyết nó không có tác động đến thuật toán. Một .ToList () đơn giản đảm bảo kết quả được thu thập trước khi bước tiếp theo giải quyết vấn đề này.

Một điều khác cần xem xét là tác động đến hệ thống bạn đang chạy mã. Tương tác phần cứng rõ ràng có thể là một yếu tố trong các hệ thống phức tạp. Tìm đĩa IO, phân bổ bộ nhớ lớn và IO mạng. Đôi khi những điều này có thể được giải quyết hiệu quả hơn bằng cách điều chỉnh hệ thống hoặc thậm chí thay thế phần cứng.

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.