Các cách tiếp cận chống lại cơ sở mã trở nên chậm đồng đều


11

Chúng tôi đang làm việc trên cơ sở mã C ++ có kích thước vừa phải (10Mloc) mà qua các nỗ lực tối ưu hóa của chúng tôi đang trở nên chậm đồng đều .

Cơ sở mã này là một tập hợp các thư viện mà chúng tôi kết hợp để đưa chúng vào hoạt động. Khi khung chung về cách các thư viện này được phát triển, có một số điểm nhấn về hiệu suất và sau này, khi có nhiều phần được thêm vào, khung chung không thay đổi nhiều. Tối ưu hóa được thực hiện khi cần thiết và khi phần cứng của chúng tôi phát triển. Điều này làm cho quyết định sớm đắt tiền rõ ràng chỉ nhiều sau đó. Bây giờ chúng ta đang ở thời điểm tối ưu hóa hơn nữa đắt hơn nhiều vì chúng sẽ yêu cầu viết lại các phần lớn của cơ sở mã. Chúng tôi thấy mình đang tiếp cận một mức tối thiểu cục bộ không mong muốn vì chúng tôi biết rằng về nguyên tắc, mã sẽ có thể chạy nhanh hơn nhiều.

Có phương pháp thành công nào giúp quyết định điều gì sẽ thay đổi sự phát triển của cơ sở mã theo hướng giải pháp tối ưu toàn cầu mà không dễ bị nhầm lẫn bởi các cơ hội tối ưu hóa dễ dàng không?

BIÊN TẬP

Để trả lời câu hỏi làm thế nào chúng tôi hiện hồ sơ:

Chúng tôi thực sự chỉ có 2 kịch bản khác nhau để sử dụng mã này, cả hai đều song song lúng túng. Hồ sơ được thực hiện cả với thời gian đồng hồ treo tường trung bình trên một mẫu lớn đầu vào và chạy chi tiết hơn (chi phí hướng dẫn, dự đoán sai chi nhánh và các vấn đề bộ đệm). Điều này hoạt động tốt vì chúng tôi chạy độc quyền trên các máy cực kỳ đồng nhất của chúng tôi (một cụm gồm vài nghìn máy giống hệt nhau). Vì chúng ta thường giữ cho tất cả các máy của mình bận rộn hầu hết thời gian chạy nhanh hơn có nghĩa là chúng ta có thể xem xét các công cụ mới bổ sung. Tất nhiên, vấn đề là khi các biến thể đầu vào mới xuất hiện, chúng có thể bị phạt muộn vì chúng tôi đã loại bỏ sự thiếu hiệu quả vi mô rõ ràng nhất cho các trường hợp sử dụng khác, do đó có thể thu hẹp số lượng kịch bản "chạy tối ưu".


10
10Mloc thực sự là một dự án khổng lồ
BЈовић

1
Đó là 10 triệu locus (tiền tố SI) được tính theo sloc. Tôi gọi nó là "kích thước vừa phải" bởi vì tôi không biết cái gì được coi là "lớn" ở đây.
Benjamin Bannier

5
khá chắc chắn 10 triệu là ít nhất là lớn ở khắp mọi nơi và có lẽ là rất lớn ở hầu hết các nơi.
Ryathal

1
Tuyệt vời, cảm ơn @honk Trong 10 triệu LỘC, có vẻ như bạn đang tối ưu hóa ở mức rất thấp, gần như ở cấp độ phần cứng? OOP truyền thống (AOS "mảng cấu trúc") không hiệu quả khủng khiếp trên bộ nhớ cache, bạn đã thử sắp xếp lại các lớp của mình thành SOA (cấu trúc của mảng) để các điểm dữ liệu mà mã của bạn đang hoạt động có nhất quán trong bộ nhớ không? Với nhiều máy bạn đang chạy vào tắc nghẽn liên lạc hoặc đồng bộ hóa ăn hết thời gian? Câu hỏi cuối cùng, bạn đang xử lý khối lượng dữ liệu truyền phát lớn hay đây có phải là vấn đề của các hoạt động phức tạp trên bộ dữ liệu của bạn không?
Patrick Hughes

1
Khi bạn đã nhận được nhiều mã như vậy, cơ hội sẽ chuyển từ cuộc sống tuyệt vời sang cuộc sống của bạn rằng có những sự tăng tốc tiềm năng lớn của loại không phải địa phương mà tôi đã đề cập. Nó không có sự khác biệt nếu có hàng ngàn chủ đề / quy trình. Một số tạm dừng ngẫu nhiên sẽ ngón tay họ cho bạn, hoặc chứng minh tôi sai.
Mike Dunlavey

Câu trả lời:


9

Tôi không biết một cách tiếp cận có mục đích chung cho vấn đề này, nhưng hai cách tiếp cận hơi liên quan làm việc tốt cho tôi trong quá khứ: vì thiếu điều kiện tốt hơn, tôi gọi họ tụ nhómtối ưu hóa ngang .

Cách tiếp cận Bunching là một nỗ lực thay thế một số lượng lớn các hoạt động ngắn, nhanh bằng một hoạt động đơn lẻ, chạy chậm, chuyên môn cao mà cuối cùng tạo ra kết quả tương tự.

Thí dụ: Sau khi định hình một thao tác đặc biệt chậm của trình soạn thảo quy tắc hình ảnh của chúng tôi, chúng tôi đã phát hiện ra không có "quả treo thấp": không có một thao tác nào chiếm hơn 2% thời gian thực hiện, nhưng toàn bộ hoạt động đều cảm thấy chậm chạp. Tuy nhiên, chúng tôi phát hiện ra rằng biên tập viên đã gửi một số lượng lớn yêu cầu nhỏ đến máy chủ. Mặc dù trình soạn thảo đã nhanh chóng xử lý các phản hồi riêng lẻ, số lượng tương tác yêu cầu / phản hồi có tác động nhân lên, do đó, tổng thời gian thao tác mất vài giây. Sau khi phân loại cẩn thận các tương tác của trình soạn thảo trong hoạt động dài đó, chúng tôi đã thêm một lệnh mới vào giao diện máy chủ. Lệnh bổ sung chuyên dụng hơn, vì nó chấp nhận dữ liệu cần thiết để thực hiện một tập hợp con các thao tác ngắn, khám phá các phụ thuộc dữ liệu để tìm ra bộ dữ liệu cuối cùng sẽ trả về và cung cấp phản hồi chứa thông tin cần thiết để hoàn thành tất cả các hoạt động nhỏ riêng lẻ trong một chuyến đi đến máy chủ. Điều này không làm giảm thời gian xử lý trong mã của chúng tôi, nhưng nó đã cắt giảm một độ trễ rất đáng kể do loại bỏ nhiều chuyến đi khứ hồi của máy khách-máy chủ đắt tiền.

Tối ưu hóa theo chiều ngang là một kỹ thuật liên quan khi bạn loại bỏ "sự chậm chạp" được phân phối mỏng giữa nhiều thành phần trong hệ thống của bạn bằng một tính năng cụ thể của môi trường thực thi.

Thí dụ: Sau khi định hình một hoạt động dài hạn, chúng tôi đã phát hiện ra rằng chúng tôi thực hiện rất nhiều cuộc gọi trên ranh giới miền ứng dụng (điều này đặc biệt cao đối với .NET). Chúng tôi không thể loại bỏ bất kỳ cuộc gọi nào và chúng tôi không thể kết hợp chúng lại với nhau: chúng đến vào những thời điểm khác nhau từ các phần khác nhau trong hệ thống của chúng tôi và những điều chúng yêu cầu phụ thuộc vào kết quả được trả về từ các yêu cầu trước đó. Mỗi cuộc gọi yêu cầu tuần tự hóa và giải tuần tự hóa một lượng dữ liệu tương đối nhỏ. Một lần nữa, các cuộc gọi riêng lẻ có thời lượng ngắn, nhưng số lượng rất lớn. Chúng tôi đã kết thúc việc thiết kế một sơ đồ tránh việc tuần tự hóa gần như hoàn toàn, thay thế nó bằng cách chuyển một con trỏ qua ranh giới miền ứng dụng. Đây là một chiến thắng lớn, bởi vì nhiều yêu cầu từ các lớp hoàn toàn không liên quan ngay lập tức trở nên nhanh hơn rất nhiều do áp dụng mộtgiải pháp ngang .


Cảm ơn đã chia sẻ kinh nghiệm của bạn, đây là những tối ưu hóa hữu ích cần ghi nhớ. Ngoài ra, vì họ nâng các bộ phận có vấn đề đến một nơi khác biệt, họ sẽ kiểm soát tốt hơn trong tương lai. Theo một nghĩa nào đó, họ đã đặt đúng chỗ những gì đáng lẽ phải xảy ra ngay từ đầu, giờ chỉ quay lại bằng dữ liệu cứng.
Benjamin Bannier

3

Điều này làm cho quyết định sớm đắt tiền rõ ràng chỉ nhiều sau đó. Bây giờ chúng ta đang ở thời điểm tối ưu hóa hơn nữa đắt hơn nhiều vì chúng sẽ yêu cầu viết lại phần lớn của cơ sở mã.

Khi bạn bắt đầu viết lại, bạn phải làm một số điều khác nhau.

Đầu tiên. Và quan trọng nhất. Dừng "tối ưu hóa". "Tối ưu hóa" hoàn toàn không thành vấn đề. Như bạn đã thấy, chỉ bán buôn viết lại vấn đề.

Vì thế.

Thứ hai. Hiểu được ý nghĩa của mọi cấu trúc dữ liệu và lựa chọn thuật toán.

Ngày thứ ba. Làm cho sự lựa chọn thực tế về cấu trúc dữ liệu và thuật toán trở thành vấn đề "ràng buộc muộn". Thiết kế giao diện có thể có bất kỳ một trong số các triển khai được sử dụng đằng sau giao diện.

Những gì bạn đang làm bây giờ (viết lại) sẽ bớt đau đớn hơn nhiều nếu bạn có một bộ giao diện được xác định cho phép bạn thực hiện thay đổi bán buôn đối với cấu trúc dữ liệu hoặc thuật toán.


1
Cảm ơn câu trả lời của bạn. Mặc dù chúng tôi vẫn cần tối ưu hóa (nằm dưới 1. và 2. đối với tôi) Tôi thực sự thích suy nghĩ có tổ chức phía sau 3. Bằng cách tạo cấu trúc dữ liệu, thuật toán & quyền truy cập được xác định muộn và rõ ràng, người ta có thể xử lý nhiều vấn đề vấn đề chúng ta đang phải đối mặt. Cảm ơn vì đã đưa nó vào một ngôn ngữ mạch lạc.
Benjamin Bannier

Bạn thực sự không cần phải tối ưu hóa. Khi bạn có cấu trúc dữ liệu chính xác, tối ưu hóa sẽ được hiển thị là một sự lãng phí công sức. Hồ sơ sẽ hiển thị nơi bạn có cấu trúc dữ liệu sai và thuật toán sai. Lừa đảo với sự khác biệt hiệu suất giữa +++=1sẽ không liên quan và gần như không thể đo lường được. Đó là điều bạn kéo dài .
S.Lott

1
Không phải tất cả các thuật toán xấu có thể được tìm thấy bằng lý luận thuần túy. Thỉnh thoảng người ta cần ngồi xuống và hồ sơ. Đây là cách duy nhất để tìm hiểu xem dự đoán ban đầu có đúng không. Đó là cách duy nhất để ước tính chi phí thực (BigO + const).
Benjamin Bannier

Hồ sơ sẽ tiết lộ các thuật toán xấu. Hoàn toàn chính xác. Đó vẫn chưa phải là "tối ưu hóa". Đó vẫn là sự điều chỉnh của một thiết kế cơ bản làm tôi thay đổi thiết kế. Tối ưu hóa (tinh chỉnh, tinh chỉnh, v.v.) sẽ hiếm khi được nhìn thấy để định hình.
S.Lott

3

Một mẹo thực tế thú vị là sử dụng bộ kiểm tra đơn vị của bạn làm bộ kiểm tra hiệu suất .

Cách tiếp cận sau đây hoạt động tốt trong các cơ sở mã của tôi:

  1. Đảm bảo phạm vi kiểm tra đơn vị của bạn là tốt (bạn đã làm điều này rồi phải không?)
  2. Hãy chắc chắn rằng bài kiểm tra của bạn đang chạy báo cáo khung thời gian chạy trên mỗi bài kiểm tra riêng lẻ . Điều này rất quan trọng vì bạn muốn tìm nơi hiệu suất chậm đang diễn ra.
  3. Nếu một bài kiểm tra đang chạy chậm, hãy sử dụng điều này như một cách để đi sâu vào và tối ưu hóa mục tiêu tại khu vực này . Tối ưu hóa trước khi xác định một vấn đề hiệu suất có thể được coi là sớm, vì vậy điều tuyệt vời của phương pháp này là bạn có được bằng chứng cụ thể về hiệu suất kém trước tiên. Nếu cần, hãy chia bài kiểm tra thành các bài kiểm tra nhỏ hơn để đánh giá các khía cạnh khác nhau để bạn có thể xác định vấn đề gốc ở đâu.
  4. Nếu một bài kiểm tra chạy cực nhanh thường tốt, mặc dù sau đó bạn có thể cân nhắc chạy thử trong một vòng lặp với các tham số khác nhau. Điều này làm cho nó trở thành một thử nghiệm hiệu suất tốt hơn và cũng làm tăng phạm vi kiểm tra của bạn về không gian tham số.
  5. Viết một vài thử nghiệm bổ sung cụ thể nhắm mục tiêu hiệu suất, ví dụ: thời gian giao dịch đầu cuối hoặc thời gian để hoàn thành 1.000 ứng dụng quy tắc. Nếu bạn có các yêu cầu hiệu suất phi chức năng cụ thể (ví dụ: <300ms thời gian phản hồi), thì sẽ khiến thử nghiệm thất bại nếu mất quá nhiều thời gian.

Nếu bạn tiếp tục làm tất cả những điều này, thì theo thời gian, hiệu suất trung bình của cơ sở mã của bạn sẽ cải thiện một cách hữu cơ.

Cũng có thể theo dõi thời gian thử nghiệm lịch sử và vẽ biểu đồ hiệu suất và hồi quy tại chỗ theo thời gian trong hiệu suất trung bình. Tôi không bao giờ bận tâm với điều này chủ yếu vì nó hơi khó để đảm bảo bạn đang so sánh giống như khi bạn thay đổi và thêm các bài kiểm tra mới, nhưng nó có thể là một bài tập thú vị nếu hiệu suất đủ quan trọng với bạn.


Tôi thích kỹ thuật - thông minh - tuyệt vời, đây là tối ưu hóa vi mô và sẽ cải thiện đến mức tối thiểu cục bộ - nó sẽ không giải quyết được các vấn đề kiến ​​trúc cho phép bạn đạt mức tối thiểu toàn cầu
jasonk

@jasonk - bạn hoàn toàn đúng. Mặc dù tôi sẽ nói thêm rằng đôi khi nó có thể cung cấp cho bạn bằng chứng bạn cần chứng minh tại sao một sự thay đổi kiến ​​trúc cụ thể là hợp lý .....
mikera

1

Câu trả lời của @dasblinkenlight chỉ ra một vấn đề rất phổ biến, đặc biệt là với các cơ sở mã lớn (theo kinh nghiệm của tôi). Có thể có vấn đề hiệu suất nghiêm trọng, nhưng chúng không được bản địa hóa . Nếu bạn chạy một trình hồ sơ, không có thói quen mất đủ thời gian, để thu hút sự chú ý. (Giả sử bạn nhìn vào tỷ lệ phần trăm thời gian bao gồm cả calle. Thậm chí đừng bận tâm đến "thời gian tự".)

Trong thực tế, trong trường hợp đó, vấn đề thực tế không được tìm thấy bằng cách định hình, mà bằng cái nhìn sâu sắc may mắn.

Tôi đã có một nghiên cứu trường hợp, trong đó có một trình chiếu PDF ngắn gọn , minh họa vấn đề này một cách chi tiết và cách xử lý nó. Điểm cơ bản là, vì mã chậm hơn nhiều so với khả năng của nó, điều đó có nghĩa là (theo định nghĩa), thời gian dư thừa được dành để làm một cái gì đó có thể được gỡ bỏ.

Nếu bạn đã xem xét một số mẫu thời gian ngẫu nhiên về trạng thái của chương trình, bạn sẽ chỉ thấy nó thực hiện hoạt động có thể tháo rời, vì phần trăm thời gian cần thiết. Hoàn toàn có thể hoạt động di động không bị giới hạn trong một chức năng, hoặc thậm chí nhiều chức năng. Nó không nội địa hóa theo cách đó.

Đó không phải là một "điểm nóng".

Đó là một mô tả bạn thực hiện về những gì bạn nhìn thấy, điều đó xảy ra đúng với một tỷ lệ lớn thời gian. Điều đó làm cho nó đơn giản để khám phá, nhưng nó có dễ sửa hay không phụ thuộc vào mức độ viết lại của nó.

(Có một bài phê bình thường được thực hiện theo cách tiếp cận này, rằng số lượng mẫu quá nhỏ so với hiệu lực thống kê. Điều đó được trả lời trên slide 13 của PDF. Nói ngắn gọn - vâng, có sự không chắc chắn cao trong "phép đo" về mức tiết kiệm tiềm năng, nhưng 1) giá trị kỳ vọng của khoản tiết kiệm tiềm năng đó về cơ bản không bị ảnh hưởng và 2) khi khoản tiết kiệm tiềm năng $ x $ được chuyển thành tỷ lệ tăng tốc theo $ 1 / (1-x) $, nó bị lệch mạnh về các yếu tố cao (có lợi).)


Cảm ơn câu trả lời của bạn. Chúng tôi không tin vào việc lấy mẫu thống kê và sử dụng thiết bị đo với valgrind. Điều này mang lại cho chúng tôi những ước tính tốt về cả chi phí "tự" và "bao gồm" của hầu hết mọi thứ chúng tôi làm.
Benjamin Bannier

@honk: Phải rồi. Nhưng thật đáng buồn, thiết bị vẫn mang ý tưởng rằng các vấn đề về hiệu suất được bản địa hóa và do đó có thể được tìm thấy bằng cách đo một phần thời gian dành cho thói quen, v.v. Bạn chắc chắn có thể chạy valgrind, v.v. .
Mike Dunlavey
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.